【问题标题】:Nested view model structure causing error in a ko.computed嵌套视图模型结构导致 ko.computed 错误
【发布时间】:2015-01-11 19:49:05
【问题描述】:

我有一个 javascript viewModel,它由一个包含项目列表的“模型”数据集启动。因为我想从我的页面在项目之间切换,所以我在订阅中收集所有其他数据。 “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 */
        });
   });
}

而在中:

<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)。

【问题讨论】:

  • 好的,谢谢。让我知道你是否想出办法。我对这个 MVVM/javascript 很新鲜,所以我可能缺乏一些基本的了解。
  • 我怀疑var mappedTextbatches 你没有对那个局部变量做任何事情。您是否在代码编辑中排除了另一个 mappedTextbatches 声明?
  • 我对 mappedTextbatches 没有任何问题。 Textbatches-array 在 selectedProject.subscribe 运行后获取值。
  • 问题是getjson是异步的

标签: javascript mvvm knockout.js


【解决方案1】:

您的问题与“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 });

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

一个全面的解决方案可能需要根据每个对象的内容重新设计您的数据结构,但我无法从您的帖子中确切地知道每个属性应该代表什么。

【讨论】:

  • 没问题!也许如果我用另一种方式解释它就会有意义。最重要的是您需要将 mappedTextbatches 移动到与使用它设置 self.Textbatches 的位置相同的级别。现在它在您的 getJson 函数中,因此 self.Textbatches 无权访问它。当您继续设置 self.Textbatches 时,它会说:“好的,将其设置为未定义。”稍后,当您尝试访问嵌套属性时,它会说:“嘿,这个对象本身是未定义的。我无法访问未定义内的属性。”
  • 正如 Origineil 指出的那样,我的示例缺少 $getJSON 的结尾括号。在我的代码中“self.Textbatches(mappedTextbatches);”是在 $.getJSON 的回调中完成的,所以我猜这不是问题?
  • 我看到您已经更改了上面的代码,但仍然遇到同样的问题。我想如果你通读 jsFiddle 你就能理解。这是另一个可能有帮助的参考资料(看看他们如何解释对 carName 变量的访问):w3schools.com/js/js_scope.asp
  • chrisjsherm self.Textbatches(mappedtextbatches) 在 $.getJSON 调用中设置。它确实得到了正确的数据。我的麻烦是 self.selectedText()。
  • 您还有另一个语法错误。顺便说一句,考虑使用 JavaScript linter 来帮助捕获这些容易遗漏的错误。它不仅可以帮助您,还可以帮助我们不必猜测您打算在哪里创建诸如闭包之类的东西。
【解决方案2】:

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

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

【讨论】:

  • 确保 T 大写是返回新的 Textbatch
  • 是的。错误仍然存​​在于“return item.parentID === batch.parentID;”中
【解决方案3】:

“范围”问题是一个两部分的问题,不是实际问题。

  • 最初提供的示例缺少$.getJSON 的一些闭括号。
  • $.map 的闭包位置与 $.getJSON 的位置相似,并且在视觉上表明实际上并不存在范围界定问题。

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

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

另一个example fiddle

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-09-29
    • 1970-01-01
    • 2017-04-10
    • 1970-01-01
    • 1970-01-01
    • 2016-06-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多