После того, как все это записано, мне становится ясно, что это может значительно превысить объем вашего вопроса. Если это действительно так, я извиняюсь; Я надеюсь, что вы все равно сможете получить от этого какую-то ценность.
Этот материал появился из настоящего приложения, над которым я работал уже несколько месяцев. Это быстрое и грязное извлечение и может содержать ошибки или опечатки, когда я удалял код приложения или упрощал его, чтобы было легче следовать.
С ней я могу
- произвольно гнездо ViewModels
- динамически добавлять ViewModels на лету
- рендеринга шаблонов Knockout, связанные с этим вложенным ViewModels, и использовать результаты гибко
Вот краткий обзор того, как это работает.
Притворись, что вы собираетесь создать приложение, отображающее список сообщений. Пользователь может нажать на сообщение, чтобы открыть модальный диалог и ответить. У нас есть три ViewModels:
- корень ViewModel называется
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
, a Child
или оба одновременно. Here is the source for them.
Родители
Если ViewModel должны быть в состоянии иметь детей ViewModels, он заимствует Parent
конструктор:
app.viewmodels.Main = function Main() {
app.viewmodels.Parent.apply(this);
this.currentUser = //.. imagine the current user being loaded here from somewhere
};
Как родительском ViewModel, Main
приобрел три вещи:
.addChildVm(string)
: добавьте детскую модель, передав ее имя. Он автоматически просматривается в пространстве имен app.viewmodel
.
.getVm(name)
: возвращает дочерний ViewModel с именем «имя»
._childVms
: наблюдаемый список, содержащий все детей
Дети
Каждый ViewModel кроме корня Main
, по крайней мере ребенок ViewModel. 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);
};
};
В детстве ViewModel, MessageList
прибыль:
- возможность получить доступ к его родителю через
this._parentVm
- необязательного
init
функция, которая автоматически вызывается родителем, если настоящим
Итак, когда мы добавили MessageList
в Main
с
messageListVm = mainVm.addChildVm('MessageList');
, Main
- создал новый экземпляр
MessageList
- добавил экземпляр в своих собственных детей
- и назвал Чайлдс
init
Ребенок тогда поставил перед собой путем получения ссылки на текущего пользователя, который поддерживается родителем Main
видмодель.
Наш последний ViewModel: MessageReply
MessageReply
просто ребенок ViewModel; как и его родительский 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
ожидает, пока указанная модель просмотра не будет доступна перед привязкой ее дочерних элементов. Он не изменяет контекст привязки.
<div data-bind="waitForVm: 'MessageList'">
<!-- bindings in here are not executed until 'MessageList' is loaded -->
<div data-bind="childVm: 'MessageList'"> ... </div>
</div>
«модальное» связывание
Source code
Это занимает шаблон Нокаут, женится на ней к ViewModel, делает его и передает результат внешней библиотеки JS, который обрабатывает модальным диалог.
Представьте себе, что это модальный библиотека
- при инициализации, создает DOM контейнер перед
</body>
- , когда просят показать модальный, принимает этот контейнер и показывает, что он накладывается на остальной части страницы, 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
будет
- использовать родительский ViewModel
MessageList
, найденный в нашем текущем связывающем контексте в $parent
- спросить его через
getVm()
для своего ребенка ViewModel например MessageReply
- добавить щелчок привязку к
<p>
, который при активировано
- называет
setup()
по телефону MessageReply
, передавая его нашим $data
- текущую мессу GE пользователь нажал на
- готовит модальный и
- делает шаблон «сообщение-ответ-шаблон», связанный с
MessageReply
ViewModel, в контейнер модальности DOM
Я не понимаю что вы пытаетесь сделать. – woz
Я думаю, что некоторая информация здесь актуальна: http://stackoverflow.com/questions/8676988/example-of-knockoutjs-pattern-for-multi-view-applications/8680668#8680668. В принципе, вы можете использовать 'ko.applyBindings' для отдельных элементов DOM с вашими индивидуальными моделями просмотров или создать модель представления уровня приложения, содержащую модели вашего дочернего представления и использовать привязку' template' к ним. –
Я делаю именно это в приложении, в котором я сейчас работаю - Viewmodels, который может иметь произвольные «дочерние режимы просмотра» и привязку, которая позволяет мне сказать «визуализировать этот шаблон с помощью этой детской модели просмотра». Я хочу упаковать его как библиотеку в Github, но пока не добрался до нее. Дайте мне знать, если вы заинтересованы - я могу быстро что-то собрать. – janfoeh