Наблюдаемые массивы JSViews не обновляют представление при удалении/добавлении и сортировке

Я пытаюсь перемещать элементы из одного массива в другой и сортировать их по имени.

эта часть работает нормально, и кажется, что observable.refresh обновляет сами данные, но представление по-прежнему показывает старые данные, использование moveFromTo() перемещает элементы, но не обновляет их порядок в представлении.

moveFromToType2() покажет добавленные элементы, но не обновит удаленный.

В обоих случаях выполнение view.refresh() решает проблему, но я не думаю, что это предполагаемое поведение.

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>

person AKHorizon    schedule 09.10.2019    source источник


Ответы (1)


Проблема в том, что вы вносите смесь невидимых и наблюдаемых изменений в to и from. Вам нужно вносить в эти массивы только наблюдаемые изменения, если вы хотите, чтобы пользовательский интерфейс обновлялся корректно благодаря связыванию данных.

Вот версия, которая исправляет это, сначала клонируя каждый массив, затем внося ненаблюдаемые изменения в клоны, а затем, наконец, передавая клоны в refresh(), чтобы сделать наблюдаемые обновления (включая все изменения):

https://jsfiddle.net/BorisMoore/qsvhm4x6/4/

function moveFromTo(from, to, index) {
    if (from.length == 0) return;
    let newFrom = from.slice(0); // Make a clone of from array
    if (index !== undefined) {
        let selected = index.map(d => newFrom[d]);
        selected.forEach(d => {
            let i = newFrom.indexOf(d);
            newFrom.splice(i, 1); // Remove selected objects from clone
        });
        let newTo = to.concat(selected); // Make a clone of to array, plus selected objects
        newTo.sort((a, b) => a.name.localeCompare(b.name)); // Sort
        newFrom.sort((a, b) => a.name.localeCompare(b.name)); // Sort
        $.observable(to).refresh(newTo); // Now observably update 'to', to the new array
        $.observable(from).refresh(newFrom); // Now observably update 'from', the new array
    }
}

А вот другой подход, использующий встроенную функцию сортировки в {^{for}}, а не чем сортировка базовых данных. Он также использует привязку данных к элементам <select>:

https://jsfiddle.net/BorisMoore/f70pnkwa/11/

<select id="leftSelect" multiple size="10" data-link="leftSelect">
    {^{for left sort="name"}}
        <option data-link="value{:id} {:name}"></option>
    {{/for}}
</select>
person BorisMoore    schedule 13.10.2019
comment
Раньше я использовал для сортировки, но это привело к тому, что моя логика перехода от к сломалась, поскольку я использовал индекс вместо идентификатора элемента, поэтому второе решение идеально. Спасибо за помощь - person AKHorizon; 14.10.2019