KnockOutJS - 单个视图中的多个视图模型

203

我认为我的应用程序现在变得非常庞大,无法使用单个ViewModel处理每个View。

因此,我想知道创建多个ViewModel并将它们全部加载到单个View中会有多难。需要注意的是,我还需要能够将X ViewModel数据传递给Y ViewModel数据,因此各个ViewModel需要能够相互通信或至少知道彼此存在。

例如,我有一个<select>下拉列表,该下拉列表具有选择状态,允许我将所选项目的ID传递给另一个Ajax调用中的单独的ViewModel....

对于在单个View中处理多个ViewModel的任何建议都将不胜感激 :)


12
对于那些看到这个问题的人,请向下滚动查看已接受的答案之后。Knockout现在支持多个绑定上下文。不需要一个巨大的“masterVM”。 - Carrie Kendall
5个回答

290

Knockout现在支持多个模型绑定。 ko.applyBindings()方法接受一个可选参数 - 绑定将被激活的元素及其后代。

例如:

ko.applyBindings(myViewModel, document.getElementById('someElementId'))

这将限制激活范围只在具有 ID 为 someElementId 的元素及其后代元素之内。

更多详细信息,请参见文档


72
如果您想使用jQuery选择器,您需要添加[0]以指定实际的DOM元素(而不是jQuery对象),例如: ko.applyBindings(myViewModel, $('#someElementId')[0]) - MrBoJangles
3
这应该是被接受的答案。你仍然可以使用一个类似当前被接受答案的主对象,然后将各个视图模型绑定到它们在页面上对应的元素上。这将有助于提高性能,并限制数据绑定所需的作用域。 - Kevin Heidt
这种方法是否可以让viewModels相互通信?例如,我有TaskVM和NoteVM。任务可以有笔记。因此,我的TaskVM必须有一个observableArray,即notes,其类型为TaskVM。您能否分享一个类似的示例? - ahmet
最好在一个新的问题中询问虚拟机之间的通信。 - Richard Nalezynski

150

如果它们需要在同一页上显示,一种简单的方法是使用一个包含其他视图模型数组(或属性列表)的主视图模型。

masterVM = {
    vmA : new VmA(),
    vmB : new VmB(),
    vmC : new VmC(),
}

如果需要,您的 masterVM 可以有其他属性,用于页面本身。在这种情况下,视图模型之间的通信不会很困难,因为您可以通过 masterVM 中继,或者您可以在绑定中使用 $parent / $root ,或一些其他自定义选项。


2
那我能不能这样做:data-bind="text: masterVM.vmA",我想我仍然可以使用附加了DOM元素的ko.applyBindings。我猜这也意味着我可以这样写:data-bind="$parent.masterVm"? - CLiown
12
您可以使用with:绑定,这样您就不会重复自己。 - AlfeG
4
是的,如果你绑定到主VM,你可以这样做。当你进入子视图模型时,你也可以使用“with”绑定来帮助避免点语法。 - John Papa
1
我认为这种方法非常限制...现在在我的情况下,我正在使用ASP.Net MVC4,但这并没有帮助,因为部分视图有自己的ViewModels,并且部分/内容部分不应该相互干扰,由于条件渲染,使用这种方法将非常困难。 - bhuvin
1
@bhuvin 使用 <!-- ko stopBinding: true --> 可以帮助你处理多个视图模型和部分视图的问题。更多信息请参见 http://www.knockmeout.net/2012/05/quick-tip-skip-binding.html。 - Micaël Félix
显示剩余3条评论

24

这是我在完成一个包含大量ViewModel的单一视图(View)项目后的回答。

Html视图

    <!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
</head>
<body>
    <div id="container1">
        <ul>
            <li >Container1 item</li>
            <!-- ko foreach: myItems -->
            <li>Item <span data-bind="text: $data"></span></li>
            <!-- /ko -->
        </ul>
    </div>

    <div id="container2">
        <ul>
            <li >Container2 item</li>
            <!-- ko foreach: myItems -->
                <li>Item <span data-bind="text: $data"></span></li>
            <!-- /ko -->
        </ul>
    </div>

    <script src="js/jquery-1.11.1.js"></script>
    <script src="js/knockout-3.0.0.js"></script>
    <script src="js/DataFunction.js"></script>
    <script src="js/Container1ViewModel.js"></script>
    <script src="js/Container2ViewModel.js"></script>

</body>
</html>

为了这个视图,我在两个独立的 JavaScript 文件中为 id=container1 和 id=container2 创建了两个视图模型。

Container1ViewModel.js

function Container1ViewModel()
{
    var self = this;
    self.myItems = ko.observableArray();
    self.myItems.push("ABC");
    self.myItems.push("CDE");

} 

Container2ViewModel.js

function Container2ViewModel() {
    var self = this;
    self.myItems = ko.observableArray();
    self.myItems.push("XYZ");
    self.myItems.push("PQR");

}

那么在 DataFunction.js 中将这两个视图模型单独注册后

var container1VM;
var container2VM;

$(document).ready(function() {

    if ($.isEmptyObject(container1VM)) {
        container1VM = new Container1ViewModel();
        ko.applyBindings(container1VM, document.getElementById("container1"));
    }

    if ($.isEmptyObject(container2VM)) {
        container2VM = new Container2ViewModel();
        ko.applyBindings(container2VM, document.getElementById("container2"));
    }
});

像这样,您可以为不同的div添加任意数量的视图模型。但是请注意,不要为注册的div内部的div创建单独的视图模型。


是否可以在其他视图模型内部完成这种操作,而不是将其作为DOM的单独元素? - UserEsp
这个解决方案对我来说是最好的。我正在开发一个复杂的用户界面,需要生成大量的表单,这些表单可以根据用户输入的条件和收集到的数据提供的上下文线索进行更改。所有其他解决方案都在某种程度上限制了我。 - snowYetis

4

6
这种方法相对于只使用ko.applyBindings(viewModel, document.getElementById("divName"))有什么优势?这不只是语法糖吗? - Paolo del Mundo
1
它还添加了对LiveQuery插件的依赖。 - Lars Gyrup Brink Nielsen
@PaolodelMundo 该插件的目的是以声明性方式使用一组视图模型。 - Sergey Zwezdin

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接