写完所有这些之后,我发现这可能会超出你的问题的范围。如果确实如此,我表示歉意;我希望你仍然可以从中获得一些价值。
这里的东西来自我一直在努力几个月的一个真正的应用程序。这是一个快速而肮脏的提取,可能包含错误或拼写错误,我删除了特定于应用的代码或将其简化以便更易于遵循。
有了它,我可以
- 任意巢的ViewModels
- 在飞行动态添加的ViewModels
- 渲染势必这些嵌套的ViewModels淘汰赛模板,使用效果灵活
以下简要介绍它的工作原理。
假装一秒钟,您将构建一个显示消息列表的应用程序。用户可以点击消息打开模式对话框并回复。我们有三个的ViewModels:
- 称为
Main
- 一个
MessageList
根视图模型是需要显示的消息
- 第三个叫
MessageReply
,负责回复功能列表的照顾。
我们所有的viewmodel构造函数在app.viewmodels
中整齐地命名空间。让我们来设置它们:
$(document).ready(function() {
var mainVm,
messageListVm,
messageReplyVm;
// we start with Main as the root viewmodel
mainVm = new app.viewmodels.Main();
// MessageList is a child of Main
messageListVm = mainVm.addChildVm('MessageList');
// and MessageReply in turn is a child of MessageList
messageReplyVm = messageListVm.addChildVm('MessageReply');
// the root is the only one that gets bound directly
ko.applyBindings(mainVm);
});
我们的标记看起来是这样的:
<body>
<!-- context here: the Main viewmodel -->
<div data-bind="childVm: 'MessageList'">
<!-- context here: the MessageList viewmodel -->
<ul data-bind="foreach: messages">
<!-- context here: the individual message object -->
<li>
<p data-bind="text: body, modal: {viewmodelName: 'MessageReply', parentViewmodel: $parent, setupViewmodelWith: $data, templateName: 'message-reply-template'}">
</p>
</li>
</ul>
</div>
</body>
<script id="message-reply-template" type="text/html">
<!-- context here: the MessageReply viewmodel -->
<div>
<textarea data-bind="value: message().body"></textarea>
<input type="submit" data-bind="click: submit">
</div>
</script>
里面还有两个自定义绑定,childVm
和modal
。前者只是查找子视图模型并将其设置为绑定上下文,而modal
绑定负责在正确的上下文中呈现模板,并将结果移交给单独的JS库。
Viewmodels通过借用构造函数Parent
或Child
或两者同时获得嵌套的能力。 Here is the source for them。
父母
如果一个视图模型应该能够有孩子的ViewModels,它借用了Parent
构造:
app.viewmodels.Main = function Main() {
app.viewmodels.Parent.apply(this);
this.currentUser = //.. imagine the current user being loaded here from somewhere
};
作为家长视图模型,Main
已经获得了三件事情:
.addChildVm(string)
:通过传递它的名称来添加一个子viewmodel。它会自动在app.viewmodel
命名空间中查找。
.getVm(name)
:返回名为“名称”
._childVms
子视图模型:包含所有的孩子
儿童
每次从根视图模型除了Main
至少一个孩子的观察的名单视图模型。 MessageList
既是Main
的孩子,也是MessageReply
的父母。它非常适合其名称,它包含要在列表中显示的消息。
app.viewmodels.MessageList = function MessageList() {
app.viewmodels.Parent.apply(this);
app.viewmodels.Child.apply(this);
// children need to set this, so we can find them by name through .getVm()
this._viewmodelName = function() { return "MessageList"; };
this.currentUser = null;
this.messages = ko.observableArray([]);
this.init = function init() {
that.currentUser = that._parentVm.currentUser;
var messages = GetMessages() // pseudocode - load our messages from somewhere
this.messages(messages);
};
};
小时候视图模型,MessageList
收益:
- 通过
this._parentVm
- 可选
init
功能,这是由父(如果存在)
自动调用访问其父的能力
所以上面我们加MessageList
到Main
与
messageListVm = mainVm.addChildVm('MessageList');
,Main
- 创建的
MessageList
- 一个新的实例添加实例到自己的孩子
- ,并呼吁孩子的
init
孩子接盘本身通过获得对由父级维护的当前用户的引用Main
viewmodel。
我们最后的视图模型:在MessageReply
MessageReply
只是个孩子视图模型;就像它的父母MessageList
自己做的一样,它也会在初始化时复制当前用户。它期望从模式绑定中传递一个Message对象,然后创建一个新消息来回复它。该答复可以通过模式中的表单进行编辑和提交。
app.viewmodels.MessageReply = function MessageReply() {
app.viewmodels.Child.apply(this);
this._viewmodelName = function() { return "MessageReply"; };
var that = this;
this.currentUser = null;
// called automatically by the parent MessageList
this.init = function init() {
that.currentUser = that._parentVm.currentUser;
};
this.messageWeAreReplyingTo = ko.observable();
// our reply
this.message = ko.observable();
// called by the 'modal' binding
this.setup = function setup(messageWeAreReplyingTo) {
// the modal binding gives us the message the user clicked on
this.messageWeAreReplyingTo(messageWeAreReplyingTo);
// imagine that Message is a model object defined somewhere else
var ourReply = new Message({
sender: that.currentUser,
recipient: that.messageWeAreReplyingTo().sender();
});
this.message(ourReply);
};
// this is triggered by the form submit button in the overlay
this.submit = function submit() {
// send the message to the server
}
};
的 'childVm' 结合
Source code
<body>
<!-- context here: the Main viewmodel -->
<div data-bind="childVm: 'MessageList'">
<!-- context here: the MessageList viewmodel -->
</div>
这仅仅是一个便利的包装围绕击倒自己的 '有:' 结合。它将一个viewmodel名称作为其值访问器,在当前绑定上下文中查找该名称的子视图模型,并使用'with:'绑定将该子项设置为新的上下文。
的“waitForVm”结合
Source code
这不是本例中使用上面,但还是比较有用的,如果你想在运行时动态添加的ViewModels,如ko.applyBindings
之前反对。这样,您可以延迟初始化应用程序的某些部分,直到用户真正想要与它们进行交互。
waitForVm
在绑定其子元素之前一直等待指定的viewmodel可用。它不会修改绑定上下文。
<div data-bind="waitForVm: 'MessageList'">
<!-- bindings in here are not executed until 'MessageList' is loaded -->
<div data-bind="childVm: 'MessageList'"> ... </div>
</div>
的“模态”结合
Source code
这需要一个敲除模板,它娶到视图模型,使得它并将结果传递到处理该模态外部JS库对话。
试想一下,当记者问到显示模式这个模式库
- 初始化的时候,创建
</body>
- 前一个DOM容器,借此容器,并显示它叠加在页面的其余部分,lightbox-风格
让我们看看在模态结合的行动再次:
<!-- context here: the individual message object -->
<li>
<p data-bind="text: body, modal: {viewmodelName: 'MessageReply', parentViewmodel: $parent, setupViewmodelWith: $data, templateName: 'message-reply-template'}">
</p>
</li>
modal
将
- 使用父视图模型
MessageList
,在我国现行的结合上下文中找到在$parent
- 通过
getVm()
其子视图模型的实例MessageReply
- 问它添加一个单击绑定到
<p>
,当其激活
- 拨打电话
setup()
在MessageReply
,交给我们$data
- 当前messa GE用户点击
- 准备模式和
- 渲染模板“消息回复模板”,绑定到
MessageReply
视图模型,成模态DOM容器
我不明白你在做什么。 – woz
我认为这里的一些信息是相关的:http://stackoverflow.com/questions/8676988/example-of-knockoutjs-pattern-for-multi-view-applications/8680668#8680668。基本上,您可以使用'ko.applyBindings'对单个视图模型中的单个DOM元素或创建一个包含您的子视图模型并使用'template'绑定的“app”级别视图模型。 –
我正在做一个我正在处理的应用程序 - 可以具有任意“子视图模型”的ViewModels,以及允许我说“使用该子视图模型呈现此模板”的绑定。我想在Github上将它打包成一个库,但还没有到它。如果您有兴趣,请告诉我 - 我可以快速地组合一些东西。 – janfoeh