2015-09-30 95 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