使用 ko.mapping.fromJS 的意外行为
Unexpected behavior using ko.mapping.fromJS
我正在试验 ko.mapping.fromJS
(http://knockoutjs.com/documentation/plugins-mapping.html)
鉴于下面的代码和预期,有人可以向我解释为什么实际输出不同吗?
var obj = { name: "frederick", minions: [{id:1, name:'Alice'},{id:2, name:'Bob'}] }
var model = {
'minions': {
key: function(data) { return ko.utils.unwrapObservable(data.id); }
}
}
var vm = ko.mapping.fromJS(obj, model);
vm.name.subscribe(x=>console.log("Changed name"));
vm.minions.subscribe(x => console.log("changed minions"));
vm.minions()[0].id.subscribe(x => console.log("changed id [0]"));
vm.minions()[0].name.subscribe(x => console.log("changed name [0]"));
vm.minions()[1].id.subscribe(x => console.log("changed id [1]"));
vm.minions()[1].name.subscribe(x => console.log("changed name [1]"));
obj.minions[0].name = 'Charlie';
ko.mapping.fromJS(obj, vm);
预期日志记录:
changed name [0]
实际记录:
changed name [0]
changed minions
问题:
由于数组中没有添加或删除记录,为什么我会看到 "Changed minions"?这些事件是否总是冒泡,或者仅在数组和直接子对象(/行)的情况下冒泡?
(还是我模型弄错了?我可以'fix'这个吗?)
幕后发生的事情是这样的:
var a = { a: 1 };
var b = { b: 2 };
var arr1 = [a, b];
var obsArray = ko.observableArray(arr2);
obsArray.subscribe(_ => console.log("change"));
var arr2 = [a, b];
// Logs change, since
// arr1 !== arr2
// even though:
// arr1[0] === arr2[0] and
// arr1[1] === arr2[1]
obsArray([a, b]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
我想说解决这个问题的最简单方法是在您的订阅方法中检查 "equality",但这主要是因为我不太了解映射插件...
我会如何解决:
- 创建一个带有
observableArray
和回调函数的辅助函数。
- helper 定义了一个
equalityCheck
接受两个数组作为输入和 returns true
或 false
- 帮助程序创建对 observable 的常规订阅,但仅在相等性检查通过时才调用传递的回调。
要将传递给淘汰订阅的新值与前一个值进行比较,它需要存储对前一个值的引用。
这意味着您的语法将从:
vm.minions.subscribe(x => console.log("changed minions"));
至:
customSubscribe(vm.minions, x => console.log("changed minions"));
可能还有一种方法可以通过插件或扩展程序来解决这个问题...
const obsArraySubThatChecksInner = (arr, cb) => {
let prevValues = arr();
// Check if items are equal without checking the array reference
const equalityCheck = (arr1, arr2) => {
return arr1.length === arr2.length &&
arr1.every((item, i) => item === arr2[i]);
}
return arr.subscribe(newValues => {
// Make a copy to prevent mutations in cb
const newArray = newValues.slice();
if (!equalityCheck(prevValues, newValues)) {
cb(newValues);
};
prevValues = newArray;
});
};
var obsArr = ko.observableArray([1, 2, 3]);
var regularSub = obsArr.subscribe(vals => console.log("Regular sub:", JSON.stringify(vals)));
var specialSub = obsArraySubThatChecksInner(
obsArr, vals => console.log("Special sub:", JSON.stringify(vals))
);
// Is "similar", so will only trigger the regular sub.
obsArr([1, 2, 3]);
// Is different, so will trigger both
obsArr([1, 2]);
obsArr.push(3);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
您的数据在 fiddle 中:https://jsfiddle.net/nk074pb3/
我正在试验 ko.mapping.fromJS (http://knockoutjs.com/documentation/plugins-mapping.html)
鉴于下面的代码和预期,有人可以向我解释为什么实际输出不同吗?
var obj = { name: "frederick", minions: [{id:1, name:'Alice'},{id:2, name:'Bob'}] }
var model = {
'minions': {
key: function(data) { return ko.utils.unwrapObservable(data.id); }
}
}
var vm = ko.mapping.fromJS(obj, model);
vm.name.subscribe(x=>console.log("Changed name"));
vm.minions.subscribe(x => console.log("changed minions"));
vm.minions()[0].id.subscribe(x => console.log("changed id [0]"));
vm.minions()[0].name.subscribe(x => console.log("changed name [0]"));
vm.minions()[1].id.subscribe(x => console.log("changed id [1]"));
vm.minions()[1].name.subscribe(x => console.log("changed name [1]"));
obj.minions[0].name = 'Charlie';
ko.mapping.fromJS(obj, vm);
预期日志记录:
changed name [0]
实际记录:
changed name [0]
changed minions
问题: 由于数组中没有添加或删除记录,为什么我会看到 "Changed minions"?这些事件是否总是冒泡,或者仅在数组和直接子对象(/行)的情况下冒泡? (还是我模型弄错了?我可以'fix'这个吗?)
幕后发生的事情是这样的:
var a = { a: 1 };
var b = { b: 2 };
var arr1 = [a, b];
var obsArray = ko.observableArray(arr2);
obsArray.subscribe(_ => console.log("change"));
var arr2 = [a, b];
// Logs change, since
// arr1 !== arr2
// even though:
// arr1[0] === arr2[0] and
// arr1[1] === arr2[1]
obsArray([a, b]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
我想说解决这个问题的最简单方法是在您的订阅方法中检查 "equality",但这主要是因为我不太了解映射插件...
我会如何解决:
- 创建一个带有
observableArray
和回调函数的辅助函数。 - helper 定义了一个
equalityCheck
接受两个数组作为输入和 returnstrue
或false
- 帮助程序创建对 observable 的常规订阅,但仅在相等性检查通过时才调用传递的回调。
要将传递给淘汰订阅的新值与前一个值进行比较,它需要存储对前一个值的引用。
这意味着您的语法将从:
vm.minions.subscribe(x => console.log("changed minions"));
至:
customSubscribe(vm.minions, x => console.log("changed minions"));
可能还有一种方法可以通过插件或扩展程序来解决这个问题...
const obsArraySubThatChecksInner = (arr, cb) => {
let prevValues = arr();
// Check if items are equal without checking the array reference
const equalityCheck = (arr1, arr2) => {
return arr1.length === arr2.length &&
arr1.every((item, i) => item === arr2[i]);
}
return arr.subscribe(newValues => {
// Make a copy to prevent mutations in cb
const newArray = newValues.slice();
if (!equalityCheck(prevValues, newValues)) {
cb(newValues);
};
prevValues = newArray;
});
};
var obsArr = ko.observableArray([1, 2, 3]);
var regularSub = obsArr.subscribe(vals => console.log("Regular sub:", JSON.stringify(vals)));
var specialSub = obsArraySubThatChecksInner(
obsArr, vals => console.log("Special sub:", JSON.stringify(vals))
);
// Is "similar", so will only trigger the regular sub.
obsArr([1, 2, 3]);
// Is different, so will trigger both
obsArr([1, 2]);
obsArr.push(3);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
您的数据在 fiddle 中:https://jsfiddle.net/nk074pb3/