Refresh observableArray when items are not observables

Basically I have an observableArray and the values for each item are not an observable. This means when I change an item value the UI within a foreach loop of the observableArray does not update accordingly.

It means a massive change if I have to set them to observable, so is there a way I can refresh either the UI or observableArray foreach manually?


Solution 1:

Yes, you can call valueHasMutated function for your array:

yourArray.valueHasMutated();

EDIT: If first didn't help you can do 'dirty' refresh:

self.refresh = function(){
    var data = self.array().slice(0);
    self.array([]);
    self.array(data);
};

Here is working fiddle: http://jsfiddle.net/vyshniakov/FuEy6/2/

Solution 2:

When you have an observable array with non observable elements, and some properties of one of the elements in the array changes, if you want to refresh only that element, you can use indexOf and splice, like this:

var changedIdx = obsArray.indexOf(changedItem);
obsArray.splice(changedIdx , 1); // removes the item from the array
obsArray.splice(changedIdx , 0, changedItem); // adds it back

What this does, is looking for the element in the array, removing it, and inserting it back. When it's inserted back, the element is bound again, with the new values.

If you like this solution, you can extend the functionality of all ko observable arrays, like this:

ko.observableArray.fn.refresh = function (item) {
    var index = this['indexOf'](item);
    if (index >= 0) {
        this.splice(index, 1);
        this.splice(index, 0, item);
    }
}

Then, when you change an item of an array, you simply have to make this call:

obsArray.refresh(changedItem);

If you have many elements in your array you'll get improved performace with respect to the dirty refresh by Artem Vyshniakov, which updates the bindings for all the elements in the array, and not only for the changed one.

Note: the valueHasMutated, appart from being undocumented (and for internal use, I suppose), only checks if the array itself has changed, not if the non-observable elements in the array have changed, so it doesn't work. I.e. it only detects if you have added elements to the array, removed elements from the array, changed elements of the array with new, different elements, or changed the order of the elements. But it doesn't check if the elements themselves have changed

Solution 3:

I ended up using the dirty approach from above but made it so that all my observable arrays had this functionality.

ko.observableArray.fn.refresh = function () {
        var data = this().slice(0);
        this([]);
        this(data);
    };

The valueHasMutated didn't work for me. Its kind of lame the entire list has to be updated. Or in my case find a way to extend the ko mapping plugin to fill the observable arrays with observable objects..