寫完所有這些之後,我發現這可能會超出你的問題的範圍。如果確實如此,我表示歉意;我希望你仍然可以從中獲得一些價值。
這裏的東西來自我一直在努力幾個月的一個真正的應用程序。這是一個快速而骯髒的提取,可能包含錯誤或拼寫錯誤,我刪除了特定於應用的代碼或將其簡化以便更易於遵循。
有了它,我可以
- 任意巢的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