TonyH TonyH - 1 year ago 37
jQuery Question

Observable Array Sort Issue with Knockout

I am having an issue displaying an observable array while using bootstraps panel-group as an accordion, however the same list displays correctly when displayed as an unordered list, sorting by SortOrder value. The sort button is used to simulate data returned from the server and, without manually sorting any of the panels, you can press this button and both list update as expected. Once you manually sort the panels, the panels will no longer sort correctly after pressing the sort button, but the unordered list continues sorting correctly.

Any suggestions on what I am doing wrong that is preventing the bootstrap panels from displaying correctly after a manual sort?



$(function() {
ko.applyBindings(new schedulerViewModel(new ViewModel()));
});

function Group(id, sortOrder, name) {
var self = this;
self.id = ko.observable(id);
self.sortOrder = ko.observable(sortOrder);
self.name = ko.observable(name);
};

function ViewModel() {
var self = this;

self.groups = ko.observableArray([
new Group(3, 0, 'System Group'),
new Group(27, 1, 'New Group Added'),
new Group(1, 2, 'Group A'),
new Group(2, 3, 'Group B')
]);
};
ko.bindingHandlers.uiSortableList = {
init: function(element, valueAccessor, allBindingAccessor, context) {
var $element = $(element),
list = valueAccessor();
$element.sortable({
axis: 'y',
handle: '#panel-handle',
stop: function(event, ui) {
var newIdOrder = $(this).sortable('toArray');
context.updateWorkgroupsSortOrder(newIdOrder);
context.sortWorkgroups();
}
});
}
};

function schedulerViewModel(viewModel) {
var self = this;
self.comments = ko.observableArray([]);
self.workgroups = ko.observableArray(viewModel.groups());
self.sortWorkgroups = function() {
self.workgroups.sort(function(a, b) {
var aSortOrder = a.sortOrder(),
bSortOrder = b.sortOrder(),
result = 0;
result = parseInt(aSortOrder) - parseInt(bSortOrder);
return result;
});
};
self.count = ko.observable(0);
self.sortButton = function() {
var array;
self.count(Math.floor((Math.random() * 10)));
if (self.count() == 0) {
array = [27, 2, 3, 1];
} else if (self.count() == 1) {
array = [3, 27, 1, 2];
} else if (self.count() == 2) {
array = [1, 3, 2, 27];
} else if (self.count() == 3) {
array = [3, 2, 27, 1];
} else if (self.count() == 4) {
array = [2, 1, 27, 3];
} else if (self.count() == 5) {
array = [1, 27, 3, 2];
} else if (self.count() == 6) {
array = [2, 3, 1, 27];
} else if (self.count() == 7) {
array = [27, 1, 3, 2];
} else if (self.count() == 8) {
array = [3, 1, 27, 2];
} else if (self.count() == 9) {
array = [1, 2, 27, 3];
} else {
array = [27, 3, 2, 1];
}
self.updateWorkgroupsSortOrder(array);
self.sortWorkgroups();
};
self.updateWorkgroupsSortOrder = function(workgroupIds) {
self.comments.push(workgroupIds);
for (var i = 0; i < workgroupIds.length; i++) {
var id = workgroupIds[i];
var workgroup = ko.utils.arrayFirst(this.workgroups(), function(item) {
return item.id() == id;
});
workgroup.sortOrder(i);
}
};
};

#panel-handle{
float:left;
clear:both;
position:relative;
display:inline-block;
top:0;
left:0;
color:gray;
height: 37px;
background-color:#f5f5f5;
-webkit-border-radius:3px;
-moz-border-radius:3px;
border-radius:3px;
border-right:solid 1px #ddd;
cursor:move;
margin-right:8px;
}
#panel-handle i{
padding: 11px 4px;
}

<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>



<div>
<button type="button" data-bind="click: sortButton">Sort (<span data-bind="text: count"></span>)</button>
</div>
<ul data-bind="foreach: workgroups, uiSortableList: workgroups">
<li><span data-bind="text: name() + ' - '"></span><span data-bind="text: sortOrder()"></span></li>
</ul>
<div id="content" class="col-xs-9 col-md-10">
<div class="panel-group" id="accordion" data-bind="foreach: workgroups, uiSortableList: workgroups">
<div data-bind="attr:{id: id() }" class="panel panel-default">
<span id="panel-handle">
<i class="fa fa-angle-double-right" aria-hidden="true"></i>
</span>
<div class="panel-heading" data-toggle="collapse" data-parent="#accordion" data-bind="attr:{href: '#work-group-' + id()}">
<h4 class="panel-title">
<span data-bind="text: name()"></span>
<span data-bind="text: '(Id) ' + id()"></span>
<span data-bind="text: ' - (SortOrder) ' + sortOrder()"></span>
</h4>
</div>
</div>
</div>
<div data-bind="foreach: comments, visible: comments().length > 0">
<div><span data-bind="text: $data"></span></div>
</div>
</div>




Answer Source

The solution was to modify the sortable stop method as follows:

stop: function(event, ui) {
    var item = ko.dataFor(ui.item[0]);
    var position = ko.utils.arrayIndexOf(ui.item.parent().children(), ui.item[0]);
    if (position >= 0) {
        list.remove(item);
        ui.item.remove();
        list.splice(position, 0, item);
    }
    var newIdOrder = $(this).sortable('toArray');
    context.updateWorkgroupsSortOrder(newIdOrder);
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download