2013-12-10 7 views
3

Я работаю с потрясающей библиотекой Knockout.js в проекте и ищу способ создания разделов моего пользовательского интерфейса во время выполнения.Динамически составляя пользовательский интерфейс с помощью Knockout.js

Например, у меня есть несколько шаблонов (упрощенных ниже), которые состоят из дочерних шаблонов. Id хотел бы передать модель представления и отобразить их, а затем сможет добавлять (и удалять) содержимое из формы критериев.

<!-- used with LineGraphModel --> 
<script type="text/html" name="linegraph-template"> 
    <div id="LineGraph"> 
     <div data-bind="contextTemplate: { name: 'series-template', data: seriesChoices, context: { selected: series } }"></div> 
     <div data-bind="contextTemplate: { name: 'xaxis-template', data: xAxisChoices, context: { selected: xaxis } }"></div> 
     <div data-bind="contextTemplate: { name: 'daterange-template', data: dateRangeChoices, context: { selected: dateRange } }"></div> 
     <div data-bind="template: { name: 'button-template', data: $data }"></div> 
    </div> 
</script> 

<!-- used with PieChartModel --> 
<script type="text/html" name="piechart-template"> 
    <div id="PieGraph"> 
    <div data-bind="contextTemplate: { name: 'series-template', data: seriesChoices, context: { selected: series } }"></div> 
    <div data-bind="contextTemplate: { name: 'daterange-template', data: dateRangeChoices, context: { selected: dateRange } }"></div> 
    <div data-bind="template: { name: 'button-template', data: $data }"></div> 
    </div> 
</script> 

я начать блуждающий по пути ko.renderTemplate, но я не могу найти любую хорошую документацию о том, как создать новый DIV и добавить результат в существующих дела. Возможно ли это, или есть другой подход, который я должен попробовать?

+1

Я не понимаю что вы пытаетесь сделать. – woz

+1

Я думаю, что некоторая информация здесь актуальна: http://stackoverflow.com/questions/8676988/example-of-knockoutjs-pattern-for-multi-view-applications/8680668#8680668. В принципе, вы можете использовать 'ko.applyBindings' для отдельных элементов DOM с вашими индивидуальными моделями просмотров или создать модель представления уровня приложения, содержащую модели вашего дочернего представления и использовать привязку' template' к ним. –

+0

Я делаю именно это в приложении, в котором я сейчас работаю - Viewmodels, который может иметь произвольные «дочерние режимы просмотра» и привязку, которая позволяет мне сказать «визуализировать этот шаблон с помощью этой детской модели просмотра». Я хочу упаковать его как библиотеку в Github, но пока не добрался до нее. Дайте мне знать, если вы заинтересованы - я могу быстро что-то собрать. – janfoeh

ответ

5

После того, как все это записано, мне становится ясно, что это может значительно превысить объем вашего вопроса. Если это действительно так, я извиняюсь; Я надеюсь, что вы все равно сможете получить от этого какую-то ценность.

Этот материал появился из настоящего приложения, над которым я работал уже несколько месяцев. Это быстрое и грязное извлечение и может содержать ошибки или опечатки, когда я удалял код приложения или упрощал его, чтобы было легче следовать.

С ней я могу

  • произвольно гнездо ViewModels
  • динамически добавлять ViewModels на лету
  • рендеринга шаблонов Knockout, связанные с этим вложенным ViewModels, и использовать результаты гибко

Вот краткий обзор того, как это работает.

Притворись, что вы собираетесь создать приложение, отображающее список сообщений. Пользователь может нажать на сообщение, чтобы открыть модальный диалог и ответить. У нас есть три ViewModels:

  1. корень ViewModel называется Main
  2. MessageList, которая заботится о отображении списка сообщений
  3. третий под названием 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 приобрел три вещи:

  1. .addChildVm(string): добавьте детскую модель, передав ее имя. Он автоматически просматривается в пространстве имен app.viewmodel.
  2. .getVm(name): возвращает дочерний ViewModel с именем «имя»
  3. ._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, который обрабатывает модальным диалог.

Представьте себе, что это модальный библиотека

  1. при инициализации, создает DOM контейнер перед </body>
  2. , когда просят показать модальный, принимает этот контейнер и показывает, что он накладывается на остальной части страницы, 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
+0

Такой большой ответ с такой деталью. Я так много узнал об этом за последний час или около того. Благодаря!! – Joe

Смежные вопросы