简体   繁体   English

valueHasMutated未按预期工作

[英]valueHasMutated not working as expected

I'm trying to force a UI update on modifying a value of an object in an observableArray , but the objects in the array aren't observable . 我正在尝试强制UI更新修改observableArray对象的observableArray ,但是数组中的对象是不可observable I assumed you could do this with valueHasMutated , but it isn't working as I expected. 我假设您可以使用valueHasMutated执行此valueHasMutated ,但它无法正常工作。

In this example if I click the update button nothing happens, but if I manually reset the array it updates: 这个例子中,如果我单击更新按钮没有任何反应,但如果我手动重置数组,它会更新:

<div id="bindings">
  <ul data-bind="foreach: observableThings">
    <li data-bind="text: id"></li>
  </ul>
</div>
<button data-bind="click: updateValue">Update</button>
<button data-bind="click: forceUpdate">Force Update</button>
var things = [
  { id: 1, thing: false },  
  { id: 2, thing: false },
  { id: 3, thing: false },
  { id: 4, thing: false }
]

var viewModel = function() {
  var self = this;

  self.observableThings = ko.observableArray(things);

  self.updateValue = function() {
    self.observableThings()[0].id = 5;
    self.observableThings.valueHasMutated();
  }

  self.forceUpdate = function() {
    self.observableThings()[0].id = 5;
    var origThings = self.observableThings();
    self.observableThings(null);
    self.observableThings(origThings);
  }
}

ko.applyBindings(new viewModel());

How is valueHasMutated supposed to work? valueHasMutated如何运作?

Sticking a bunch of plain objects into an observable array does not magically make the properties of those objects observable. 将一堆普通对象粘贴到一个可观察的数组中并不会神奇地使这些对象的属性可观察。

An observable array generally only observes item removal and item insertion. 可观察数组通常仅观察项目移除和项目插入。 If the items themselves have properties you want to observe then these properties explicitly need to be made observable. 如果项目本身具有您想要观察的属性,则显式地需要使这些属性可观察。

The mapping plugin can help with that. 映射插件可以帮助解决这个问题。 It can do a few very nice things, do read the documentation page. 它可以做一些非常好的事情,请阅读文档页面。

 function ListOfThings(params) { var self = this; self.things = ko.observableArray(); self.updateValue = function() { ko.utils.arrayForEach(self.things(), function (thing) { thing.id( ~~(Math.random() * 100) ); }); } // init ko.mapping.fromJS(params, {}, self); } var vm = new ListOfThings({ things: [ { id: 1, thing: false }, { id: 2, thing: false }, { id: 3, thing: false }, { id: 4, thing: false } ] }); ko.applyBindings(vm); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script> <div id="bindings"> <ul data-bind="foreach: things"> <li data-bind="text: id"></li> </ul> </div> <button data-bind="click: updateValue">Update</button> <hr> <pre data-bind="text: ko.toJSON($root, null, 2)"></pre> 


EDIT You seem to be disproportionally fixed on valueHasMutated and the impossibility of using the mapping plugin. 编辑你似乎不成比例地修复了valueHasMutated以及使用映射插件的不可能性。 The mapping plugin is helpful, but it's certainly not necessary. 映射插件很有用,但肯定没有必要。

 function Thing(params) { this.id = ko.observable(params.id); this.thing = ko.observable(params.thing); } function ListOfThings(params) { var self = this; self.things = ko.observableArray(); self.updateValue = function() { ko.utils.arrayForEach(self.things(), function (thing) { thing.id( ~~(Math.random() * 100) ); }); } // init self.things(ko.utils.arrayMap(params.things, function (obj) { return new Thing(obj); })); } var vm = new ListOfThings({ things: [ { id: 1, thing: false }, { id: 2, thing: false }, { id: 3, thing: false }, { id: 4, thing: false } ] }); ko.applyBindings(vm); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script> <div id="bindings"> <ul data-bind="foreach: things"> <li data-bind="text: id"></li> </ul> </div> <button data-bind="click: updateValue">Update</button> <hr> <pre data-bind="text: ko.toJSON($root, null, 2)"></pre> 

KnockoutJS code (thanks to debugger version in your fiddle), executing on valueHasMutated leads to KnockoutJS代码(感谢你的小提琴中的调试版),在valueHasMutated上执行导致

evaluateImmediate_CallReadWithDependencyDetection: function (notifyChange) {
// some other code
// ...

// newValue is the same as state.latestValue => "computedObservable["notifySubscribers"]" is not called
        if (computedObservable.isDifferent(state.latestValue, newValue)) {
            if (!state.isSleeping) {
                computedObservable["notifySubscribers"](state.latestValue, "beforeChange");
            }

            state.latestValue = newValue;

            if (state.isSleeping) {
                computedObservable.updateVersion();
            } else if (notifyChange) {
                computedObservable["notifySubscribers"](state.latestValue);
            }
        }

This can do the trick: 这可以做到这一点:

  self.updateValue = function() {
    var index = 0;
    var item = self.observableThings()[index];
    // Update item content
    item.id = 5;
    // Force array to update markup
    self.observableThings.splice(index, 1);
    self.observableThings.splice(index, 0, item);
  }

But IMHO the better way is to use observable properties. 但恕我直言,更好的方法是使用可观察的属性。

valueHasMutated would typically be used if you wanted to change the contents of a large observableArray multiple times but only trigger a single change notification and a single UI update or whatever. 如果您想多次更改大型observableArray的内容但只触发单个更改通知和单个UI更新等,则通常会使用valueHasMutated So you would operate on the underlying array and then call valueHasMutated when all your changes are complete. 因此,您将对基础数组进行操作,然后在完成所有更改后调用valueHasMutated

The reason that your call to valueHasMutated isn't doing anything is because the 'value' of the observable array (meaning the items in it, not the properties of those items) has not changed. 你对valueHasMutated的调用没有做任何事情的原因是因为可观察数组的'value'(意味着它中的项,而不是那些项的属性)没有改变。 Your call causes a re-evaluation of the state of the array - have any items been added or removed? 您的呼叫会导致重新评估阵列的状态 - 是否已添加或删除任何项目? The answer is no, so no change notification happens. 答案是否定的,因此不会发生变更通知。

If you want to make your code work with the smallest change possible, you need to swap in a new item rather than just update one of the properties on an existing item. 如果您希望使用尽可能小的更改来使代码工作,则需要交换新项而不是仅更新现有项上的某个属性。

self.updateValue = function() {
    var newVal = { id: 5, thing: false };
    self.observableThings()[0] = newVal;
    self.observableThings.valueHasMutated();
  }

fiddle here 在这里小提琴

The final thing to say is that kind of the case I outlined above (ie updating many members of an array yet only triggering a sigle UI update) is really the only reason that you should be 'forcing' change notifications to happen. 最后要说的是我上面概述的那种情况(即更新阵列中的许多成员但只触发sigle UI更新)实际上是你应该“强制”改变通知发生的唯一原因。 If you're having to do something like this then you've gone wrong and saying 'but I'll have to re-write loads of code' is never a good reason not to do it properly. 如果你不得不做这样的事情那么你就错了并且说'但是我必须重新编写大量的代码'从来都不是不正确做法的好理由。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM