【问题标题】:KnockoutJS computed observable undefinedKnockoutJS 计算出未定义的 observable
【发布时间】:2014-07-03 01:59:42
【问题描述】:

我有这个非常简单的 knockoutjs 脚本。我的视图模型包含一个名为“模块”的属性,它是一个字符串数组。如果我有一个这样的 foreach 列表,它会为每个项目打印一个模块列表:

<tbody data-bind="foreach: items">
    <tr>
        <td data-bind="text: modules"></td>
    </tr>
</tbody>

但是,如果我想打印模块的数量,可以添加一个计算的 observable:

<tbody data-bind="foreach: items">
    <tr>
        <td data-bind="text: numModules"></td>
    </tr>
</tbody>

我遇到了问题。 'undefined' 不是我计算函数第一行所说的函数。我的 js 代码如下所示:

function AppViewModel(data) {
    var self = this;
    ko.mapping.fromJS(data, {}, this);
    this.numModules = ko.computed(function() {
        return self.modules().length;
    });
};

$.getJSON("/api/items", function(data) {
    var viewModel = new AppViewModel(data);
    ko.applyBindings(viewModel);
});

【问题讨论】:

  • 在您的 AppViewModel 中您没有 modules
  • 这个想法是模型由数据定义,通过使用映射插件。

标签: javascript knockout.js


【解决方案1】:

在这种情况下,计算对象可能会在属性实际存在之前尝试计算。 ko.computed 上默认未设置的一个参数是 deferEvaluation 参数...一旦设置了此参数,computed 将不会尝试在 AppViewModel 的初始化时进行计算。

this.numModules = ko.computed({ 
    read: function() {
        return self.modules().length;
    },
    deferEvaluation: true
);

更挑剔的一点是,如果您定义了“self = this”,那么在下一行设置您的计算值与“this”的上下文,您为什么要定义“self”?

【讨论】:

  • 我明白了......仍然没有理由不在其他地方使用 self 已经定义了它。
【解决方案2】:

问题是您在 ViewModel 的 root 上定义 numModules 而您尝试执行的计算是在 每个 @987654324 的 modules 属性上@

因此,self.modules 确实未定义且无法调用,因为self 指的是根对象,而modules 是每个项目的属性。

试试这个:

function AppViewModel(data)
{
    var self = this;
    ko.mapping.fromJS(data, {}, this);

    // defining the computed function on each 'item'
    for (var i in self.items())
    {
        self.items()[i].numModules = ko.computed(function()
        {
            return this.modules().length;
        }, self.items()[i]);
    }
};

或者这样:

// defined on $root
self.numModules = function(item) {
    return item.modules().length;
}

// but passing 'item' upon invocation
<td data-bind="text: $root.numModules($data)"></td>

【讨论】:

  • 在您的第一个示例中,我想避免在每个项目上定义一个函数。我宁愿以某种方式在模型上定义它。最后一个例子,这个函数真的和ko.computed一样吗?
  • ko.computed 上使用函数是很常见的,在你的情况下它也可以正常工作。请参阅stackoverflow.com/a/11528135 了解更多信息。
  • 到目前为止的答案中,您的最后一个是唯一有效的。 html 中的 $root 和 $data 语法看起来不像简单的“numModules”那么好。
  • 如果您正在寻找漂亮而干净的代码,最好的办法是实际定义您的项目 ViewModel (function item() { this.modules = ... }),而不是依赖 ko.mapping从任意数据动态创建对象。
【解决方案3】:

我发现解决问题最简单的方法,根本不用添加任何特殊的 js 代码,就是在 html 中简单地这样做:

<td data-bind="text: modules().length"></td>

【讨论】:

    【解决方案4】:

    我不确定,但如果你这样做会怎样:

    function AppViewModel(data) {
        var self = this;
    
        this.modules = ko.obeservableArray();
    
        ko.mapping.fromJS(data, {}, this.modules());
    
        this.numModules = ko.computed(function() {
            return self.modules().length;
      });
    };
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-04-17
      • 1970-01-01
      • 2014-02-27
      • 1970-01-01
      • 2016-11-03
      • 2014-10-23
      • 2016-09-29
      • 2013-11-16
      相关资源
      最近更新 更多