I am trying to move items from one array to another, and sorting them by name.
this part works fine and it seems doing observable.refresh updates the data itself but the view still shows old data, using moveFromTo() will move the items, but will not update their order in the view.
moveFromToType2() will show the items that are added, but will not update the one that removed from.
In both cases doing view.refresh() solves the issue but I don't think this is an intended behavior.
https://jsfiddle.net/y946xhvq/
<body>
<div id="multiselect"></div>
<script id="multiselectTemplate" type="text/x-jsrender">
<select id="leftSelect" multiple>
{^{for left}}
<option data-link="value{:#index}">{^{:name}}</option>
{{/for}}
</select>
<button id="rightButton"> > </button>
<button id="leftButton"> < </button>
<select id="rightSelect" multiple>
{^{for right}}
<option data-link="value{:#index}">{^{:name}}</option>
{{/for}}
</select>
</script>
<script>
var data = {
left: [{ "id": 0, "name": "Melendez Garner" }, { "id": 1, "name": "Mara Orr" }, { "id": 2, "name": "Bass Salazar" }, { "id": 3, "name": "Carol Freeman" }, { "id": 4, "name": "Selma Bradford" }, { "id": 5, "name": "Cotton Parrish" }, { "id": 6, "name": "Haley Campbell" }, { "id": 7, "name": "Ruth Wright" }, { "id": 8, "name": "Carmella Blake" }],
right: []
}
data.left.sort((a, b) => a.name.localeCompare(b.name));
var m = $.templates('#multiselectTemplate').link('#multiselect', data);
$('#multiselect').on('click', '#leftButton', function () {
//MOVE FROM RIGHT TO LEFT
var view = $.view(this);
var value = $('#rightSelect').val();
if (value === null) return;
//moveFromToType2(data.right, data.left, value);
moveFromTo(data.right, data.left, value);
//view.refresh();
}).on('click', '#rightButton', function () {
//MOVE FROM LEFT TO RIGHT
var view = $.view(this);
var value = $('#leftSelect').val();
if (value === null) return;
//moveFromToType2(data.left, data.right, value);
moveFromTo(data.left, data.right, value);
//view.refresh();
})
function moveFromToType2(from, to, index) {
if (from.length == 0) return;
if (index !== undefined) {
let selected = index.map(d => from[d]);
selected.forEach(d => {
let i = from.indexOf(d);
from.splice(i, 1);
});
$.observable(to).insert(selected);
to.sort((a, b) => a.name.localeCompare(b.name));
from.sort((a, b) => a.name.localeCompare(b.name));
}
}
function moveFromTo(from, to, index) {
if (from.length == 0) return;
if (index !== undefined) {
let selected = index.map(d => from[d]);
selected.forEach(d => {
let i = from.indexOf(d);
$.observable(from).remove(i);
});
$.observable(to).insert(selected);
to.sort((a, b) => a.name.localeCompare(b.name));
from.sort((a, b) => a.name.localeCompare(b.name));
$.observable(to).refresh(to);
$.observable(from).refresh(from);
}
}
</script>
The issue there is that you are making a mix of non-observable changes, and observable changes, to
toandfrom. You need to make only observable changes to those arrays, if you want the UI to update correctly, driven by the data-linking.Here is a version which corrects this, by first cloning each array, then making non-observable changes to the clones, then finally passing in the clones to
refresh(), to make the observable updates (incorporating all the changes):https://jsfiddle.net/BorisMoore/qsvhm4x6/4/
And here is a different approach, using the built-in sort feature on the
{^{for}}, rather than sorting the underlying data. It also uses data-linking on the<select>elements:https://jsfiddle.net/BorisMoore/f70pnkwa/11/