Как обновить наблюдаемый элемент массива в knockoutjs?

У меня есть следующий массив JavaScript,

[{"unitPrice": 2499,"currency":"$","productId":1,"retailerId":1,"productName":"XX ","formattedPrice":"$ 2,499","productImage":"Images/2012_08_12_00_45_39_4539.jpg","productQuantity":"9","totalPrice":19992},
{"unitPrice": 4999,"currency":"$","productId":2,"retailerId":1,"productName":"XX","formattedPrice":"$ 4,999","productImage":"Images/2012_08_12_00_46_45_4645.jpg","productQuantity":2,"totalPrice":9998},
{"unitPrice":4555,"currency":"$","productId":1,"retailerId":1,"productName":"XXXXX","formattedPrice":"$ 4,555","productImage":"Images/2013_02_12_10_57_49_5749_9868.png","productQuantity":3,"totalPrice":13665}] 

здесь находится соответствующий html,

<table>
<tbody data-bind="foreach: $root">
                    <tr>
                        <td><img width="45" height="45" alt="" data-bind="attr:{src: productImage}"/></td>
                        <td><span data-bind="html: productName"></span></td>
                        <td><span data-bind="html: formattedPrice"></span></td>
                        <td><input type="number" class="quantity" data-bind="value: productQuantity, attr:{'data-id': productId }"  /></td>
                        <td><span data-bind="html: totalPrice"></span></td>
                    </tr>
                </tbody>
</table>

Затем я создал наблюдаемый массив как,

observableItems = ko.observableArray(items);
ko.applyBindings(observableItems);

Теперь мне удалось получить specfic-элемент, используя

       var obj = ko.utils.arrayFirst(list(), function (item) {
            return item.productId === id;
        });

Однако, когда я меняю,

item.productQuantity = 20;

Но пользовательский интерфейс не обновляется. Пробовал также,

item.productQuantity(item.productQuantity)

Но получение ошибки productQuantity не является функцией

Ответ 1

Вышеприведенное поведение объясняется тем, что только массив является наблюдаемым, а не отдельными элементами в массиве или свойствами каждого элемента.

Когда вы выполните item.productQuantity = 20;, это приведет к обновлению свойства, но поскольку оно не является наблюдаемым, пользовательский интерфейс не обновляется.

Similary, item.productQuantity(20) дает вам ошибку, потому что productQuantity не является наблюдаемым.

Вы должны посмотреть на определение структуры объекта для каждого элемента вашего массива, а затем добавить элементы этого типа в свой наблюдаемый массив. Как только это будет сделано, вы сможете сделать что-то вроде item.productQuantity(20), и пользовательский интерфейс немедленно обновится.

EDIT Добавлена ​​функция, предоставляемая OP:). Эта функция преобразует каждое свойство элементов в массив в наблюдаемые.

function convertToObservable(list) 
{ 
    var newList = []; 
    $.each(list, function (i, obj) {
        var newObj = {}; 
        Object.keys(obj).forEach(function (key) { 
            newObj[key] = ko.observable(obj[key]); 
        }); 
        newList.push(newObj); 
    }); 
    return newList; 
}

END EDIT

Если вы не можете изменить этот фрагмент кода, вы также можете сделать что-то вроде observableItems.valueHasMutated(). Однако это не очень хорошо, поскольку он сигнализирует KO и вашему пользовательскому интерфейсу, что весь массив изменился, и пользовательский интерфейс отобразит весь массив на основе привязок.

Ответ 2

Вы можете легко использовать плагин ko.mapping, чтобы преобразовать объект в наблюдаемый: http://knockoutjs.com/documentation/plugins-mapping.html

Преобразование обычного JS-объекта в наблюдаемый объект:

var viewModel = ko.mapping.fromJS(data);

Преобразование наблюдаемого объекта в обычный JS-объект:

var unmapped = ko.mapping.toJS(viewModel);