【问题标题】:Knockout JS Calling a ViewModel function inside a foreach bindingKnockout JS 在 foreach 绑定中调用 ViewModel 函数
【发布时间】:2014-03-28 09:57:19
【问题描述】:

让我们考虑一个像这样使用敲除的视图模型:

var data = [{ id: 1, name: "John Doe" }, { id: 2, name: ""}, { id: 3, name: "Peter Parker"}];

var viewModel = {
    items: ko.observableArray(data)
};

viewModel.showName = function (name) {
    console.log(this);
    return name && name.length > 0;
};

viewModel.removePerson = function () {
    console.log(this); 
};

ko.applyBindings(viewModel);

有了这个视图:

<ul data-bind="foreach: items">
    <li><span data-bind="text: id"></span>
         <span data-bind="visible: $root.showName(name)">Yes! show the name</span>
         <a href="#" data-bind="click: $root.removePerson">Remove</a>
    </li>
</ul>

你可以在这里看到它的实际效果:http://jsfiddle.net/SmW35/8/

在这种情况下,当有人点击“删除”链接时,KO调用了showName函数, 函数里面的对象“this”,就是当前item的对象,比如我点击item 2中的“remove”,“this”就是{id: 2, name: ""} 但是,当 KO 绑定“可见”并调用 showName 函数时,“this”对象不包含当前项,您必须将“name”传递给函数(或者您可以使用 $data)。

所以,我有两个问题:

  • 有一种方法可以从视图调用 showName 函数,而无需传递名称或 $data(与删除链接类似的行为)
  • 如果没有,这样做有什么问题吗?我与一位同事进行了一次有趣的讨论,他认为这样做是不对的,因为您是从视图 ($root.showName(name)) 发送数据,然后这不是“纯”MVVM 模式。他提议创建一个自定义 KO 绑定来实现该功能。在我看来是用坦克杀死苍蝇,但我很想知道是否有不同的方法,或者你也认为我没有用我的代码做一个纯粹的 MVVM 模式。

【问题讨论】:

  • 出于兴趣,为什么要隐藏名称 span 但让按钮可见。如果您想隐藏两者,则 Luis 在下面描述了一个不同的解决方案
  • @RobertSlaney 删除按钮仅用于帮助理解我的要求。我的项目中没有删除按钮,并且 showName 用于其他目的(但完全相同)。谢谢!

标签: javascript mvvm knockout.js


【解决方案1】:

从某种意义上说,您的同事说得有道理。不过,我不会亲自创建自定义绑定来处理此问题(继续主观说明,如果视图和视图模型之间存在特殊的通信方式,则自定义绑定更有意义;请参阅this post 以了解何时使用使用它们)。

顺便说一句,如果我们探索自定义绑定的选项,我想您可以执行类似textIfNotEmpty 绑定处理程序的操作,它将textvisible 合二为一.另一方面,如果showName 功能保持简单,您也可以选择:

<span data-bind="visible: !!name, text: name"></span>

无论如何,我更喜欢以下...

根本问题是 IMO 视图模型违反了单一责任原则:showName 功能应该是表示项目的视图模型的责任。

var Item = function(data) {
    var self = this;

    self.id = data.id;

    self.name = ko.observable(data.name); 
    // or plain "self.name = data.name;" if you don't need 2way binding

    self.showName = ko.computed(function() { 
         return self.name() && self.name().length > 0;
    });
}

现在您可以像这样轻松绑定:

<ul data-bind="foreach: items">
    <li><span data-bind="text: id"></span>
         <span data-bind="visible: showName">Yes! show the name</span>
         <a href="#" data-bind="click: $root.removePerson">Remove</a>
    </li>
</ul>

这还允许您将removePerson 重写为:

viewModel.removePerson = function (person) {
    console.log(person); 
};

这确实需要您在构建 observable 数组时做一些额外的工作,但这是值得的,因为它清楚地分离了所有关注点。可以按照以下方式进行:

var viewModel = {
    items: ko.observableArray(data.map(function(item) { return new Item(item); }))
};

请参阅this fiddle 以获取上述演示。

【讨论】:

  • 非常感谢。 2 cmets:1)我猜View有错误,“可见:showName(name)”应该是“可见:showName()”。 2)我没有看到我的View有任何逻辑,逻辑在showName函数中。该视图仅发送 Name 的值。无论如何,我理解你的例子,我同意这是更纯粹的 MVVM,但也有更多的工作。不确定是否值得。再次感谢!
  • 啊,是的,我猜你在这两个方面都是对的。无论如何,SO对于讨论主观位并不是很好,所以请将我的回答仅仅视为实现您想要的另一种方式。这是一个个案(主观)决定最终采用哪种方法。祝你好运!
  • 附言。如果您在我的代码中发现任何错误或缺陷,请随时编辑我的答案。
  • 嗨@Jeroen,我是同事,我同意你有一个项目“类”并将其用作DTO。但问题是与我一起工作的团队。他们的 js 技能很低,只是想把数据数组从你得到的 json 映射到 DTO,这让我的生活变得非常复杂。无论如何,通常我使用 DTO 来解决这样的问题,并且自定义绑定是一种解决方法,以保持代码干净并尽快解决问题。在此链接中,还有我 2 年前使用的另一个 DTO 示例:github.com/Salvodif/MyPlayboxArea/blob/master/MVC/…
猜你喜欢
  • 2013-11-30
  • 2016-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-08
  • 1970-01-01
  • 2013-07-18
  • 2014-02-28
相关资源
最近更新 更多