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

时间:2016-01-27 15:50:49

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

使用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应该只复制到viewmodel。

遗憾的是,如果数据结构变得更深,它的效果不会很好:

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

似乎令人费解的是,复制某些结构是有效的,而其他结构则不然。我明白,当树中的某个地方存在多级哈希时,问题会跳进去。

这是期望的行为还是我在这里做错了什么?为什么复制某些结构而不是其他?

2 个答案:

答案 0 :(得分:5)

我已经成功地复制了你提到的问题:



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>
&#13;
&#13;
&#13;

正如您所看到的,在我的复制品中,我包含了两个库的非缩小版本。我在错误中介入了它,它在这里,在这段代码中:

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

这条线让我觉得奇怪,因为通常使用KO你需要明确地需要&#34;展开&#34;如果您不确定它们是普通值还是可观察值,则可以使用这些值。

所以我去了the relevant source code on 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;
}

这条线是我预期的方式。

然后,the most recent debug build再次出现了错误/第一版。

然后再挖掘一下,我看到那里issue #137正是这个问题。这个问题以我得出的相同结论结束:修复程序未包含在2.4.1版本中。很可能不会很快出现新版本:正如您在the project's readme中看到的那样,此项目目前尚未得到积极维护。

结论:您需要解决这种情况,或者修复此问题的自定义构建版本。

答案 1 :(得分:1)

您是否考虑过定义自定义对象构造?有关详细信息,请参阅ko文档中的advanced section in "The mapping plugin" page

该文档中包含了很多选项,用于映射到/来自JSON对象的不同情况。