KnockOutJS-单个视图中有多个ViewModel


201

我在想我的应用程序现在变得很大,太大了,无法使用单个ViewModel处理每个View。

所以我想知道创建多个ViewModels并将它们全部加载到一个View中会有多么困难。需要注意的是,我还需要能够将X ViewModel数据传递到Y ViewModel数据,以便各个ViewModel能够相互通信或至少彼此了解。

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

在单个视图中处理大量ViewModel的任何观点都表示赞赏:)


12
对于那些遇到此问题的人,请滚动到接受的答案。 淘汰赛现在支持多个绑定上下文。不需要巨人masterVM
凯莉·肯德尔

Answers:


150

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

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

然后,masterVM如果需要,您可以为页面本身设置其他属性。在这种情况下,视图模型之间的通信并不困难,因为您可以通过进行中继masterVM,也可以使用$parent/ $rootin绑定或某些其他自定义选项。


2
因此,我可以执行以下操作:data-bind =“ text:masterVM.vmA”,我想我仍然可以使用附加了DOM元素的ko.applyBindings。假设那也意味着我可以做:data-bind =“ $ parent.masterVm”?
CLiown

12
@CLiown您可以使用with:绑定,因此您不会重复自己
AlfeG 2012年

4
@CLiown是的,如果绑定到masterVM,则可以这样做。当您深入子视图模型时,还可以使用“ with”绑定来避免点语法。
约翰·帕帕

1
我认为这种方法是一种非常严格的方法...现在,在我的情况下,我使用的是ASP.Net MVC4,这无济于事,因为会有局部视图具有自己的ViewModels,而局部/内容部分不应互相干扰,并且由于条件渲染的缘故,使用这种方法确实非常困难。
bhuvin

1
@bhuvin使用<!-ko stopBinding:true->将帮助您使用多个视图模型和部分视图部分。有关更多信息,请参见kickmeout.net/2012/05/quick-tip-skip-binding.html
米卡·费利克斯

285

淘汰赛现在支持多种模型绑定。该ko.applyBindings()方法采用一个可选参数-绑定将被激活的元素及其后代。

例如:

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

这将激活限制为具有ID的元素someElementId及其后代。

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


72
如果您希望使用jQuery选择器,则需要添加[0]以指定实际的DOM元素(而不是jQuery对象),如下所示:ko.applyBindings(myViewModel, $('#someElementId')[0])
MrBoJangles 2013年

3
这应该是公认的答案。您仍然可以像当前接受的答案一样使用主对象,然后将各个视图模型绑定到页面上的相应元素。这将节省性能,并限制数据绑定所需的范围。
Kevin Heidt 2014年

是否可以通过这种方法互相交流viewModels?即我有TaskVM和NoteVM。任务可以有注释。因此,我的TaskVM必须具有一个observableArray,即类型为TaskVM的注释。您可以分享一个这样的例子吗?
ahmet

最好在新问题中询问VM之间的通信。
理查德·纳列辛斯基

21

这是在单个视图中完成具有很多ViewModels的非常大的项目后的答案。

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创建2个视图模型。

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添加任意数量的viewmodel。但是请确保不要在已注册的div中为div创建单独的视图模型。


是否可以在其他视图模型内部做某种视图模型,而不是将其作为DOM的单独元素?
UserEsp

4

检查Knockout JS的MultiModels插件-https: //github.com/sergun/Knockout-MultiModels


6
与ko.applyBindings(viewModel,document.getElementById(“ divName”))相比,这有什么优势?不只是语法糖吗?
Paolo del Mundo

1
@Paolo del Mundo它还增加了对LiveQuery插件的依赖。
拉尔斯·吉鲁普·布林克·尼尔森2013年

@PaolodelMundo插件的目的是能够以无比例方式使用一组视图模型
Sergey Zwezdin 2014年

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.