KnockoutJS 3.X API 第五章 高级应用(2) 控制后代绑定
注意:这是一种高级技术,通常仅在创建可重用绑定的库时使用。
默认情况下,绑定仅影响它们应用到的元素。 但是如果你想影响所有的后代元素呢?
为此,只需从绑定的init函数中返回{controlsDescendantBindings:true}即可。
示例1:控制是否应用后代绑定
对于一个非常简单的例子,这里有一个名为allowBindings的自定义绑定,允许后代绑定仅当它的值为true时才应用。 如果值为false,则allowBindings告诉Knockout它负责后代绑定,因此它们不会像往常一样绑定。
ko.bindingHandlers.allowBindings = { init: function(elem, valueAccessor) { // Let bindings proceed as normal *only if* my value is false var shouldAllowBindings = ko.unwrap(valueAccessor()); return { controlsDescendantBindings: !shouldAllowBindings }; } };
要使此效果生效,以下是一个示例用法:
<div data-bind="allowBindings: true"> <!-- This will display Replacement, because bindings are applied --> <div data-bind="text: 'Replacement'">Original</div> </div> <div data-bind="allowBindings: false"> <!-- This will display Original, because bindings are not applied --> <div data-bind="text: 'Replacement'">Original</div> </div>
示例2:为子孙绑定提供附加值
通常,使用controlsDescendantBindings的绑定也将调用ko.applyBindingsToDescendants(someBindingContext,element)来对一些修改的绑定上下文应用后代绑定。 例如,您可以使用一个名为withProperties的绑定将一些额外的属性附加到绑定上下文,然后可用于所有后代绑定:
ko.bindingHandlers.withProperties = { init: function(element, valueAccessor, allBindings, viewModel, bindingContext) { // Make a modified binding context, with a extra properties, and apply it to descendant elements var innerBindingContext = bindingContext.extend(valueAccessor); ko.applyBindingsToDescendants(innerBindingContext, element); // Also tell KO *not* to bind the descendants itself, otherwise they will be bound twice return { controlsDescendantBindings: true }; } };
正如你可以看到,绑定上下文有一个扩展函数,产生一个带有额外属性的克隆。 extend函数接受具有要复制的属性的对象或返回此类对象的函数。 函数语法是首选的,以便将来在绑定值中的更改始终在绑定上下文中更新。 此过程不会影响原始绑定上下文,因此不会影响同级元素的危险 - 它只会影响后代。
以下是使用上述自定义绑定的示例:
<div data-bind="withProperties: { emotion: 'happy' }"> Today I feel <span data-bind="text: emotion"></span>. <!-- Displays: happy --> </div> <div data-bind="withProperties: { emotion: 'whimsical' }"> Today I feel <span data-bind="text: emotion"></span>. <!-- Displays: whimsical --> </div>
示例3:在绑定上下文层次结构中添加额外的级别
绑定(如with和foreach)在绑定上下文层次结构中创建额外的级别。 这意味着它们的后代可以通过使用$ parent,$ parents,$ root或$ parentContext来访问外部级别的数据。
如果你想在自定义绑定中这样做,那么不使用bindingContext.extend(),使用bindingContext.createChildContext(someData)。 这返回一个新的绑定上下文,其viewmodel是someData,其$ parentContext是bindingContext。 如果需要,您可以使用ko.utils.extend扩展具有额外属性的子上下文。 例如,
ko.bindingHandlers.withProperties = { init: function(element, valueAccessor, allBindings, viewModel, bindingContext) { // Make a modified binding context, with a extra properties, and apply it to descendant elements var childBindingContext = bindingContext.createChildContext( bindingContext.$rawData, null, // Optionally, pass a string here as an alias for the data item in descendant contexts function(context) { ko.utils.extend(context, valueAccessor()); }); ko.applyBindingsToDescendants(childBindingContext, element); // Also tell KO *not* to bind the descendants itself, otherwise they will be bound twice return { controlsDescendantBindings: true }; } };
这个更新的withProperties绑定现在可以以嵌套方式使用,每个嵌套级别都可以通过$ parentContext访问父级别:
<div data-bind="withProperties: { displayMode: 'twoColumn' }"> The outer display mode is <span data-bind="text: displayMode"></span>. <div data-bind="withProperties: { displayMode: 'doubleWidth' }"> The inner display mode is <span data-bind="text: displayMode"></span>, but I haven't forgotten that the outer display mode is <span data-bind="text: $parentContext.displayMode"></span>. </div> </div>
通过修改绑定上下文和控制后代绑定,一个强大的和高级的工具来创建自己的自定义绑定机制。