2013-12-10 12 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