嵌套视图模型结构导致ko.computed中的错误

时间:2014-11-14 12:07:32

标签: javascript mvvm knockout.js

我有一个javascript viewModel,它是使用包含项目列表的“model”数据集启动的。由于我想从我的页面切换项目,我收集订阅中的所有其他数据。 “selectedProject”定义<select>中的选定值。 我通过将selectedProject启动到Projects-array中的第一个元素来首次触发订阅。

不知何故,我收到错误消息

TypeError: batch is undefined
return item.parentID === batch.parentID; 
^ 

我无法理解为什么。

这是我的代码:

ViewModel = function (model)  {
    var self = this;

    self.selectedProject = ko.observable();
    self.selectedText = ko.observable();
    self.Textbatches = ko.observableArray();

    self.Projects = ko.observableArray(
        ko.utils.arrayMap(model, function (item) {
            return new Project(item);
        }));

    self.selectedProject.subscribe(function (project) { 

        /* ...some code... */ 

        $.getJSON("myService/GetTextbatchesFromDB", "projectID=" + project.projectID,
            function (allData) {
               var mappedTextbatches = $.map(allData, function (item) {
                    return new Textbatch(item);
               });
               self.Textbatches(mappedTextbatches);

               self.selectedText(self.Textbatches()[0]);

            }
         ); // End of $.getJSON

        /* ...some more code... */
    }

    self.selectedProject(self.Projects()[0]);  


    self.parentIDFilteredTextbatches = ko.computed(function () {
        var batch = self.selectedText();
        return ko.utils.arrayFilter(self.Textbatches(), function (item) {
            return item.parentID === batch.parentID;   /* ERROR ON THIS LINE */
        });
   });
}

并且在&lt; HTML&GT;:

<script>

    // some code getting "model from the db...

    var viewModel = new ViewModel(model);
    ko.applyBindings(viewModel);
</script>

我发现如果我在self.selectedText()初始化后移动self.selectedProject.subscribe-function中的“self.parentIDFilteredTextbatches = ko.computed()” - functon,它将在页面第一次时起作用加载。

但是,我不喜欢这样的视图模型结构(也许我会习惯它),更糟糕的是,我在选择新项目时遇到了同样的错误(即,触发了selectedProject.subscription)。 / p>

3 个答案:

答案 0 :(得分:2)

你的问题与&#34; mappedTextbatches&#34;的范围有关。变量。可能还有轻微的语法错误。

由于后者的范围,视图模型的Textbatches属性无法访问mappedTextbatches变量。稍后当您尝试访问parentID的{​​{1}}属性(作为Textbatches[0]属性中的局部变量集)时,您会收到未定义的错误,因为selectedText实际上从未实际集。

有关进一步说明和示例,请参阅此jsFiddle:http://jsfiddle.net/0p5fftcc/

Textbatches

我无法对此进行测试以确保,但我猜测如果您将代码更改为以下内容,则不应再收到错误。

/* Demonstrates a similar behavior to 
 * your current view model.
 */
var currentViewModel = function () {

var self = this;

self.personName = ko.observable('Bob');
self.personAge = ko.observable(10);
self.hasPersonNameChanged = ko.observable(false);

self.personName.subscribe(function (newValue) {
    self.hasPersonNameChanged(true);

    (function fakeGetJSON() {
        /* The local age variable is within the
         * function closure, so personAge cannot
         * get access to it.
         */
        var localAge = 20;
    })();

    self.personAge(localAge);
});
};

修改以反映帖子中的新代码

在评估之前,请检查var mappedTextbatches = $.getJSON("myService/GetTextbatchesFromDB", "projectID=" + project.projectID, function (allData) { return $.map(allData, function (item) { return new Textbatch(item); }); }); 是否有值。见下文。

selectedText

虽然我无法确定所提供信息的方式,但我认为 self.parentIDFilteredTextbatches = ko.computed(function () { if (self.selectedText() !== undefined) { var batch = self.selectedText(); return ko.utils.arrayFilter(self.Textbatches(), function (item) { return item.parentID === batch.parentID; /* ERROR ON THIS LINE */ }); } return; }); 可以与客户进行互动。假设这一点但又不知道更多,对您的问题的一个不太理想的,虽然快速的解决方法是将self.selectedText属性声明更改为视图模型顶部的以下内容。这会降低对计算函数的通知速度,该函数保留对TextbatchesTextbatches的订阅,希望能够为selectedText设置足够的时间。

selectedText

这也将绕过您稍后会遇到的问题,即如果您刚刚实施了以后的客户端更新,则新self.Textbatches = ko.observableArray().extend({ rateLimit: 500 }); 和旧Textbatches之间存在不匹配检查selectedText

全面的解决方案可能涉及重新设计每个对象所包含的数据结构,但我无法从您的帖子中准确地告诉每个属性应该代表什么。

答案 1 :(得分:0)

克里斯给出了正确的答案。更具体地说:

$.getJSON("myService/GetTextbatchesFromDB", "projectID=" + project.projectID,
    function (allData) {
        var mappedTextbatches = $.map(allData, function (item) {
            return new Textbatch(item);
});
self.Textbatches(mappedTextbatches);

应该是这个

var mappedTextbatches;
$.getJSON("myService/GetTextbatchesFromDB", "projectID=" + project.projectID,
    function (allData) {
        mappedTextbatches = $.map(allData, function (item) {
            return new Textbatch(item);
});
self.Textbatches(mappedTextbatches);

答案 2 :(得分:0)

&#34;范围&#34;问题是一个两部分问题,不是一个实际问题。

  • 原始提供的示例缺少$.getJSON的一些闭包括号。
  • $.map关闭时的定位看起来像$.getJSON,并直观地显示了实际上并不存在的范围问题。

问题是selectedTextTextBatches无关,并且由于订阅尚未( synchronicity )填充{{},因此手动设置为undefined 1}}。一旦订阅强制对计算的TextBatches进行评估仍然具有selectedText,现在undefined具有实际数据,然后导致TextBatches回调执行。

this example中的控制台语句说明了代码中的事件序列。

另一个example fiddle