javascript - 使用 ko.mapping.fromJS 的意外行为

标签 javascript knockout.js knockout-mapping-plugin knockout-3.0

我正在尝试 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”?这些事件是否总是冒泡,或者仅在数组和直接子对象(/行)的情况下? (或者我在模型中犯了错误?我可以“修复”这个问题吗?)

最佳答案

幕后发生的事情是这样的:

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>

我想说解决这个问题的最简单方法是检查订阅方法中的“平等”,但这主要是因为我不太了解映射插件......

我如何解决这个问题:

  • 创建一个采用 observableArray 和回调函数的辅助函数。
  • 帮助器定义一个 equalityCheck,它接受两个数组作为输入并返回 truefalse
  • 帮助器创建对可观察对象的常规订阅,但仅在相等性检查通过时才调用传递的回调。

要将传递给 knockout 订阅的新值与前一个值进行比较,它需要存储对前一个值的引用。

这意味着您的语法将从:

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/

关于javascript - 使用 ko.mapping.fromJS 的意外行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43779310/

相关文章:

javascript - Nvd3 多条形图 - 在 xAxis 上显示数据间隙

javascript - 通过jquery删除选中的属性.model值在knockout js中未更新

javascript - 是否可以将 child 作为参数传递给 ko 计算函数

javascript - knockout 映射创建/更新时出现问题

javascript - 为什么 knockout.mapping 在某些数据结构上失败?

javascript - 需要正则表达式来访问字符串

javascript - 无需 Facebook Connect 即可访问 Facebook Open Graph

javascript - 如何在部署后立即自动在客户端浏览器上加载更改?

mvvm - 你能强制 KnockoutJS 重新评估绑定(bind)吗?

javascript - 遍历对象属性