Refresh observableArray when items are not observa

2020-01-29 07:35发布

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?

标签: knockout.js
5条回答
兄弟一词,经得起流年.
2楼-- · 2020-01-29 07:48

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

查看更多
祖国的老花朵
3楼-- · 2020-01-29 07:49

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..

查看更多
祖国的老花朵
4楼-- · 2020-01-29 07:50

I'm using Knockout with deferUpdates and the solution of JotaBe needs an update.

It seems Knockout detects, on remove/add, that is the same item and so don't refresh the array.

I have adopted the following solution:

ko.observableArray.fn.refresh = function (item, index) {
    if (index==null) index = this['indexOf'](item);
    if (index >= 0) {
        this.splice(index, 1, ko.utils.extend({}, item)) // create new item
        //this.splice(index, 1);
        //this.splice(index, 0, item);
    }
}

and it refresh the array correctly! :-)

Note I added a second argument to refresh function, for specifying index when index is given, for avoiding indexOf run.

查看更多
时光不老,我们不散
5楼-- · 2020-01-29 07:55

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/

查看更多
闹够了就滚
6楼-- · 2020-01-29 08:10

I found a simple solution by replacing the entire object in the array.

In this example parameter ix is the index, oLine is the updated object and oVM.objProps.lines contains the array. The solution worked like a champ.

/* This contortion causes the computed function to fire because
 * we have replaced the entire line object.
 */
function forceRefresh(ix,oLine) {
  var oVM = pme.getVM();
  oLine = JSON.parse(JSON.stringify(oLine));
  oVM.oObjProp.lines[ix] = oLine;
}
查看更多
登录 后发表回答