2015-09-30 93 views
1

我正在單頁面應用程序中工作,我們正在使用Knockout相當廣泛。我們目前有一個可以點擊的項目列表,這樣他們會將一些內容加載到一個模式容器中。下面的圖片說明會觸發各種內容不同的項目進行顯示:延遲加載自定義綁定

enter image description here

這些容器的內容實質上不同,並且可以分佈在多個標籤頁的不同自定義綁定。圖像中的項目非常簡單,只需使用Knockout組件,但是當我們開始顯示模式內容時,它們在JavaScript上更加沉重,因此使用綁定。

我最近添加了組件所需的JavaScript和HTML模板的延遲加載,這非常有效。我不得不使用自定義組件加載程序,因爲我們不想使用require或類似的AMD模塊加載程序。

現在我面臨着與定製淘汰賽綁定相同的問題,因爲我預計隨着此產品的擴展,我們可以非常輕鬆地結束100個綁定。不幸的是,似乎沒有一種明顯的方式來像組件那樣以一種懶惰的方式加載自定義綁定,我試圖找出是否有辦法做到這一點,以及最好的方式是什麼。請注意,我也一直不知道綁定的名稱,有時我可能希望根據observable的名稱動態加載它們。

到目前爲止我唯一能找到的注意事項是有一個ko.getBindingHandler()函數可以被覆蓋,但它需要一個綁定處理程序的同步加載。


我已經想好了辦法,試圖做到這一點的,但它使用的部件,感覺就像實現我的最終目標的一個非常落後的方式。它會是這樣的:

更換一個慣用的手法結合:

<div data-bind="lineChart: $data"/> 

<div data-bind="component { name: compName, params: { vm: $data } }"/> 

然後我會使用一個自定義組件加載器,這實際上是隻裝載結合處理程序JavaScript,並寫出基本上佔位符div與自定義綁定在:

var bindingLoader = { 
    getConfig: function(name, callback) { 
     if(name.startsWith("binding-")) { 
     callback({ binding: name.replace("binding-", ""), jsUrl: "/bindings/" + name }); 
     return; 
     } 
     callback(null); 
    }, 
    loadComponent(name, componentConfig, callback) { 
    var obj = { }; 
    obj.template = '<div data-bind="' + componentConfig.name + ': $data"/>'; 
    $.ajax({ url: componentConfig.jsUrl, dataType: "text" }) 
     .done(function(data)) { 
      (new Function(data))(); 
      callback(obj); 
    }); 
    } 
} 

但是我確定有一個更好的方法來實現這個目標,但我現在想不出其他的選擇。

回答

1

我也在Github上回答了這個問題。

@Jeroen是正確的,沒有內置的方式異步加載自定義綁定。但是任何綁定都可以「懶惰地」執行自己的操作,這就是綁定的作用。通過覆蓋ko.getBindingHandler,我們可以檢測尚未加載的綁定,並啓動加載過程,然後返回一個封裝器綁定處理程序,該處理器在加載後應用綁定。

var originalGetBindingHandler = ko.getBindingHandler; 
ko.getBindingHandler = function (bindingKey) { 
    // If the binding handler is already known about then return it now 
    var handler = originalGetBindingHandler(bindingKey); 
    if (handler) { 
     return handler; 
    } 

    if (bindingKey.startsWith("dl-")) { 
     bindingKey = bindingKey.replace("dl-", ""); 
     if (ko.bindingHandlers[bindingKey]) { 
      return ko.bindingHandlers[bindingKey]; 
     } 

     // Work out the URL at which the binding handler should be loaded 
     var url = customBindingUrl(bindingKey); 

     // Load the binding from the URL 
     var loading = $.getScript(url); 

     return ko.bindingHandlers["dl-" + bindingKey] = { 
      init: function (element, valueAccessor, allBindings, viewModel, bindingContext) { 
       // Once the binding is loaded, apply it to the element 
       loading.done(function() { 
        var binding = {}; 
        binding[bindingKey] = valueAccessor; 
        ko.applyBindingAccessorsToNode(element, binding); 
       }); 
       // Assumes that all dynamically loaded bindings will want to control descendant bindings 
       return { controlsDescendantBindings: true }; 
      } 
     } 
    } 
}; 

http://jsfiddle.net/mbest/e718a123/

+0

我對代碼進行了更改,因爲我意識到我的原始版本每次都會重新加載綁定處理程序。 –

0

AFAIK:不,沒有通用的方式來延遲加載自定義綁定。

然而,有很多選擇,但我們不能推薦任何特定的選項,因爲它們將嚴重依賴於上下文。總結幾個例子:

  • 如果可能的話,你可以使用這些綁定內部組件,並延遲加載組件;
  • 根據綁定處理程序的作用,它本身可以延遲加載,直到最近所需的時間(例如,在init你只會註冊一個事件回調,實際上是加載你想加載的東西);
  • 如果您正確使用if綁定,那麼在需要之前,將不會評估其中的任何自定義綁定。對於foreach綁定也是如此,除非這些項目在那裏,否則將不會應用數組項目的自定義綁定。
  • 只有當您準備好時,您才能撥打applyBindings到DOM的特定部分。

Et cetera。但是,再一次,你的問題也是也是廣泛。用實際場景創建一個(或更多?)新問題,告訴我們爲什麼/如何需要自定義綁定來加載懶惰,並告訴我們您嘗試了哪些方法以及爲什麼他們不工作。

+0

我試圖擴大使用情況下這使這個問題更加詳細。 – Ian