Knockout.js 日本語ドキュメント

配下のバインディングを制御する

注: これは高度な技術であり、通常では再利用可能なバインディングのライブラリを作成する場合にのみ使用されます。普通、Knockout でアプリケーションを構築する際には必要になりません。

デフォルトでは、バインディングはそれが適用されたDOM要素のみに影響します。しかし、もし全ての配下の要素にも影響を与えたい場合、どうしたらよいでしょうか?これは可能です。あなたのバインディングは、配下には全くバインドしないようKnockoutに伝えることもできますし、別の方法によって、何であれ好きな対象にバインドできます。

これを行うには、単にあなたのバインディングの init 関数から、return { controlsDescendantBindings: true } を返します。

例: 配下のバインディングが適用されるかどうかを制御する

非常に簡単な例として、こちらは 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>

例: 配下のバインディングに対する追加の値の供給

通常、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 関数を持ちます。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>

例: バインディングコンテキストの階層にレベルを追加する

withforeachバインディングと同様に、バインディングコンテキストの階層に追加のレベルを作成します。これはその配下の要素が $parent$parents$root、または $parentContext の使用によって外側のレベルにあるデータにアクセスできることを意味します。

もし、あなたがこれをカスタムバインディングで行いたい場合、bindingContext.extend() の代わりに、bindingContext.createChildContext(someData) を使用してください。この関数は、ビューモデルが someData であり、$parentContextbindingContext である新しいバインディングコンテキストを返します。もし望むのであれば、その後に 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>

バインディングコンテキストを変更し、配下のバインディングを制御することは、あなた自身のカスタムバインディングの機構を作成するための、強力かつ高度なツールとなります。

原文はこちら

side menu