从服务器更新后,集合对象的Computed属性未更新问题排查
咱们先拆解你遇到的两个核心问题:
1. 初始保存后 NeedsSaving 未更新的原因
你在reason构造函数里定义了originalReason = data.Reason,这个值是初始化时的静态值,不会随着后续self.Reason的更新而改变。当你调用ko.mapping.fromJS(response.data, {}, reason)更新reason对象后,self.Reason()已经变成了服务器返回的最新值,但NeedsSaving计算属性依然在拿新的Reason和旧的originalReason做对比,自然会一直返回true,导致保存按钮无法切换。
2. 修改done函数后NeedsSaving变成普通布尔值的原因
你创建了一个新的reason实例model,然后用ko.mapping.fromJS(model, {}, reason)把新实例的属性映射到旧实例上。这里的问题是:Knockout的映射插件会遍历model的所有属性,包括你定义的NeedsSaving计算属性。当它把model.NeedsSaving(一个computed)赋值给reason.NeedsSaving时,会直接覆盖掉原来的computed,把它变成一个普通的布尔值(因为computed的执行结果是布尔值),这就导致后续UI无法再响应Reason的变化了。
正确的解决方案
我们需要在保存成功后,同步更新originalReason的值,让NeedsSaving的计算基于最新的服务器返回值,同时避免覆盖computed属性。
方案一:修改reason构造函数,添加更新原始值的方法
var reason = function(data) { let self = this; // 用observable存储原始值,方便后续更新 let originalReason = ko.observable(data.Reason); self.Reason = ko.observable(); self.IsComplete = ko.observable(false); self.NeedsSaving = ko.computed(function() { return self.Reason() !== originalReason(); }); // 添加更新原始值的方法 self.updateOriginalReason = function(newReason) { originalReason(newReason); }; ko.mapping.fromJS(data, {}, self); }
然后在saveReason的done回调里,更新完属性后调用这个方法:
self.saveReason = function (reason) { $.ajax({ // 省略配置 }).done(function (response) { // 更新reason的属性 ko.mapping.fromJS(response.data, {}, reason); // 同步更新原始值 reason.updateOriginalReason(response.data.Reason); }); };
方案二:直接在done回调里更新原始值(如果不想修改构造函数)
如果不想给reason添加新方法,可以把originalReason改成实例的一个属性(注意不要被映射覆盖):
var reason = function(data) { let self = this; // 把originalReason变成实例的属性,用下划线标记为内部属性 self._originalReason = data.Reason; self.Reason = ko.observable(); self.IsComplete = ko.observable(false); self.NeedsSaving = ko.computed(function() { return self.Reason() !== self._originalReason; }); ko.mapping.fromJS(data, {}, self); }
然后在保存回调里更新:
self.saveReason = function (reason) { $.ajax({ // 省略配置 }).done(function (response) { ko.mapping.fromJS(response.data, {}, reason); // 更新内部的原始值 reason._originalReason = response.data.Reason; // 手动触发computed重新计算(可选,因为Reason更新后computed会自动触发,但保险起见可以加) reason.NeedsSaving.notifySubscribers(); }); };
这样修改后,保存成功后NeedsSaving会基于最新的服务器返回值重新计算,UI就能正确切换到完成按钮;同时也不会出现computed被覆盖成普通值的问题。
内容的提问来源于stack exchange,提问作者JB06

