嵌套视图模型结构导致 ko.computing 错误

Nested view model structure causing error in a ko.computed

本文关键字:ko computing 错误 视图 模型 结构 嵌套      更新时间:2023-09-26

我有一个javascript viewModel,它是用一个包含项目列表的"模型"数据集启动的。由于我想从我的页面在项目之间切换,因此我在订阅中收集所有其他数据。"选定项目"定义<select>中的选定值。我第一次通过启动 selectedProject 到项目数组中的第一个元素来触发订阅。

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

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 */
        });
   });
}

在 HTML <中>:

<script>
    // some code getting "model from the db...
    var viewModel = new ViewModel(model);
    ko.applyBindings(viewModel);
</script>

我发现,如果我在初始化self.selectedText()后将"self.parentIDFilteredTextbatches = ko.computed()"-functon移动到self.selectedProject.subscribe-function中,它将在首次加载页面时工作。

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

您的问题与"mappedTextbatches"变量的范围有关。还可能存在轻微的语法错误。

视图模型的 Textbatches 属性无法访问mappedTextbatches变量,因为后者的作用域。稍后,当您尝试访问 Textbatches[0]parentID 属性(作为从 selectedText 属性设置的局部变量)时,会出现未定义的错误,因为从未实际设置Textbatches

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

/* 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可以在客户端上进行交互。假设这一点,但在不知道更多的情况下,一个不太理想(尽管很快)修复您的问题的方法是在视图模型的顶部将Textbatches属性声明更改为以下内容。这将减慢对计算函数的通知,该函数保存对TextbatchesselectedText的订阅,希望有足够的时间来设置selectedText

self.Textbatches = ko.observableArray().extend({ rateLimit: 500 });

这也将规避您以后会遇到的问题,即如果您刚刚实施undefined检查,则在后续客户端更新中,新Textbatches和旧selectedText不匹配。

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

"范围"问题是一个由两部分组成的问题,不是实际问题。

  • 原始提供的示例缺少一些用于$.getJSON的闭合大括号。
  • $.map闭合处的位置看起来像$.getJSON的位置,并且在视觉上指示实际上不存在的范围界定问题。

问题是selectedText不依赖于TextBatches并且手动设置为undefined,因为订阅尚未(同步性)填充TextBatches。一旦订阅强制对计算selectedText进行评估,它仍然具有undefined,现在TextBatches具有实际数据,然后导致执行arrayFilter回调。

此示例中的控制台语句说明了代码中的事件序列。

另一个例子小提琴。

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

$.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);