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

Why knockout.mapping fails on some data-structures?

本文关键字:数据 数据结构 结构上 失败 knockout mapping 为什么      更新时间:2023-09-26

使用knockout.mapping,我尝试从JSON映射到ko-object。当尝试使用带有映射选项的更高级解决方案时,我经常遇到Uncaught TypeError: g is not a function.我试图找出问题所在,但我仍然感到困惑。

假设我有非常简单的模型:

var data1 = { // works
  a: "a",
  b: [ 
    { b1: "v1" }, 
    { b2: "v2" } 
  ] 
};

现在使用此模型可以像这样工作:

function viewModel ( data ) {
  var self = this;
  var a    = ko.mapping.fromJS( data, { observe: "a"} );
  return a;
}
var vm1 = viewModel( data1 ); // works

我的模型中有两个属性(ab),但我只需要a就可以观察,而b应该只是复制到视图模型中。

可悲的是,如果数据结构变得更深,它就不能很好地工作:

var data1 = { // works
  a: "a",
  b: [ 
    { b1: "v1" }, 
    { b2: "v2" } 
  ] 
};
var data2 = { // Uncaught TypeError: g is not a function
  a: "a",
  b: [ 
    { b1: { name: "v1" } }, 
    { b2: { name: "v2" } } 
  ]
};
var data3 = { // Uncaught TypeError: g is not a function
  a: "a",
  b1: { name: "v1" }, 
  b2: { name: "v2" } 
};
var data4 = { // works
  a: "a",
  b1: "v1", 
  b2: "v2"  
};
var data5 = { // works
  a: "a",
  b1: ["1::v1", "2::v1"], 
  b2: ["1::v2", "2::v2"]  
};
function viewModel ( data ) {
  var self = this;
  var a    = ko.mapping.fromJS( data, { observe: "a"} );
  return a;
}
var vm1 = viewModel( data1 ); // works
var vm2 = viewModel( data2 ); // Uncaught TypeError: g is not a function
var vm3 = viewModel( data3 ); // Uncaught TypeError: g is not a function
var vm4 = viewModel( data4 ); // works
var vm5 = viewModel( data5 ); // works

复制某些结构有效而其他结构无效似乎令人困惑。我知道,当树中的某处存在多级哈希时,这个问题就会跳出来。

这是想要的行为还是我在这里出了什么问题?为什么复制某些结构有效而其他结构无效?

我已经成功地重现了您提到的问题:

var data1 = { // works
  a: "a",
  b: [ 
    { b1: "v1" }, 
    { b2: "v2" } 
  ] 
};
var data2 = { // Uncaught TypeError: g is not a function
  a: "a",
  b: [ 
    { b1: { name: "v1" } }, 
    { b2: { name: "v2" } } 
  ]
};
function viewModel ( data ) {
  var self = this;
  var a = ko.mapping.fromJS( data, { observe: ["a"]} );
  return a;
}
var vm1 = viewModel( data1 ); // works
var vm2 = viewModel( data2 ); // Uncaught TypeError: g is not a function
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-debug.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.js"></script>

如您所见,在我的重现中,我包含了两个库的非缩小版本。我在错误上介入了它,它在这里,在这段代码中:

if(options.observe.length > 0 && ko.utils.arrayIndexOf(options.observe, fullPropertyName) == -1)
{
    mappedRootObject[indexer] = value(); // <---- THIS LINE
    options.copiedProperties[fullPropertyName] = true;
    return;
}

这句话让我感到很奇怪,因为通常对于 KO,如果您不确定它们是纯值还是可观察值,则需要明确需要"解包"值。

所以我去了 Github 上的相关源代码,但令我惊讶的是,上面来自 CDNJS 版本的代码不存在,它是不同的:

if(options.observe.length > 0 && ko.utils.arrayIndexOf(options.observe, fullPropertyName) == -1)
{
    mappedRootObject[indexer] = ko.utils.unwrapObservable(value); // <-- DIFFERENT
    options.copiedProperties[fullPropertyName] = true;
    return;
}

这条线是我所期望的方式。

再说一次,最新的调试版本再次具有错误/第一个版本。

然后再挖掘一下,我看到问题 #137 正是这个问题。该问题以我得出的相同结论结束:该修复程序不包含在 2.4.1 版本中。不太可能很快有新的构建:正如您在项目的自述文件中看到的那样,该项目目前没有被积极维护。

底线:您需要解决此问题,或者创建自己的自定义映射构建并修复此问题。

您是否考虑过定义自定义对象构造?详细信息可以在ko文档的"映射插件"页面的高级部分找到。

该文档中包含相当多的选项,用于映射到/从 JSON 对象映射的不同情况。