2013-12-10 13 views
3

我正在使用项目上的令人敬畏的Knockout.js库,并且正在寻找一种方法在运行时组合我的UI部分。使用Knockout.js动态编写用户界面

例如,我有几个模板(简化,下面)由子模板组成。我喜欢将视图模型传递给它们并渲染它们,然后能够从标准形式追加(并移除)内容。

<!-- 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并将结果追加到现有的div任何好的文档。这是可能的,还是有另一种方法,我应该尝试?

+1

我不明白你在做什么。 – woz

+1

我认为这里的一些信息是相关的:http://stackoverflow.com/questions/8676988/example-of-knockoutjs-pattern-for-multi-view-applications/8680668#8680668。基本上,您可以使用'ko.applyBindings'对单个视图模型中的单个DOM元素或创建一个包含您的子视图模型并使用'template'绑定的“app”级别视图模型。 –

+0

我正在做一个我正在处理的应用程序 - 可以具有任意“子视图模型”的ViewModels,以及允许我说“使用该子视图模型呈现此模板”的绑定。我想在Github上将它打包成一个库,但还没有到它。如果您有兴趣,请告诉我 - 我可以快速地组合一些东西。 – janfoeh

回答

5

写完所有这些之后,我发现这可能会超出你的问题的范围。如果确实如此,我表示歉意;我希望你仍然可以从中获得一些价值。

这里的东西来自我一直在努力几个月的一个真正的应用程序。这是一个快速而肮脏的提取,可能包含错误或拼写错误,我删除了特定于应用的代码或将其简化以便更易于遵循。

有了它,我可以

  • 任意巢的ViewModels
  • 在飞行动态添加的ViewModels
  • 渲染势必这些嵌套的ViewModels淘汰赛模板,使用效果灵活

以下简要介绍它的工作原理。

假装一秒钟,您将构建一个显示消息列表的应用程序。用户可以点击消息打开模式对话框并回复。我们有三个的ViewModels:

  1. 称为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> 

里面还有两个自定义绑定,childVmmodal。前者只是查找子视图模型并将其设置为绑定上下文,而modal绑定负责在正确的上下文中呈现模板,并将结果移交给单独的JS库。

Viewmodels通过借用构造函数ParentChild或两者同时获得嵌套的能力。 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已经获得了三件事情:

  1. .addChildVm(string):通过传递它的名称来添加一个子viewmodel。它会自动在app.viewmodel命名空间中查找。
  2. .getVm(name):返回名为“名称”
  3. ._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功能,这是由父(如果存在)
自动调用访问其父的能力

所以上面我们加MessageListMain

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库对话。

试想一下,当记者问到显示模式这个模式库

  1. 初始化的时候,创建</body>
  2. 前一个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容器
+0

非常详细的答案。在过去的一个多小时里,我从中学到了很多东西。谢谢!! – Joe