1

親ビューモデルにobservableArray依存するノックアウト.js子ビューモデルがあります。observable親はドロップダウンにobservableバインドされており、子はその値がいつ変更されたかを知る必要があります。select

親ビュー モデル全体を だけに渡し、子を「手動で」更新するためにobservable、親の内部を にサブスクライブしました。observableこれらはすべて拘束力のある目的で機能します。

問題は、その後、子をクリアしてobservableArray新しい子用のスペースを空けるか、単純に子を新しい子に置き換えると、knockout.js によって設定された逆方向の依存関係によってまだ参照されているため、古い子がメモリ内に残ることです。

私は、別の設計パターン、またはノックアウトに依存関係の保持を停止するように指示する方法のいずれかに対してオープンです。別の例では、cleanNode() を試しましたが、これらの逆依存関係はクリーンアップされていないようです。

コード:

function FamilyViewModel(name) {
    this.name = name;
    this.children = ko.observableArray();
}

function ChildViewModel(firstName, lastName, selectedFamilyObservable) {
    this.firstName = ko.observable(firstName);
    this.lastName = ko.observable(lastName);
    this.fullName = ko.computed(function() {
        return this.firstName() + " " + this.lastName();
    }, this);
    this.isSelected = ko.computed(function() {
        return selectedFamilyObservable().name == this.lastName();
    }, this);
}

function PageViewModel() {
    this.families = ko.observableArray([
            new FamilyViewModel("Smith"),
            new FamilyViewModel("Jones"),
            new FamilyViewModel("Brown")
            ]);
    this.selectedFamily = ko.observable();
    this.addChild = _.bind(function() {
        this.selectedFamily().children.push(new ChildViewModel("Frank", this.selectedFamily().name, this.selectedFamily));
    }, this);
    this.resetChildren = _.bind(function() {
        this.selectedFamily().children([]);
    }, this);
}

$(function() {
    ko.applyBindings(new PageViewModel());
});

フィドル:

http://jsfiddle.net/cygnl7/xhzkC/1/

4

2 に答える 2

1

前に参照したFiddleソリューション。

問題の核心は、現在選択されているファミリ ( ) を追跡するだけでなく、選択され.selectedFamilyているかどうかを各ファミリに知らせたいということです。selectedFamily最も簡単な解決策は、家族の価値を設定するために、変化する前と変化した後にその値を購読して監視することだと思いisSelectedます。これを行うためにファミリー配列をループする必要がないため、このアプローチが気に入っています。

self.selectedFamily = ko.observable();
self.selectedFamily.subscribe(function (oldValue) {
    if (oldValue) {
        oldValue.isSelected(false);
    }
}, null, 'beforeChange');
self.selectedFamily.subscribe(function (newValue) {
    if (newValue) {
       newValue.isSelected(true);
    }
});

サブスクリプションの既定の機能は、値が既に変更された後に新しい値を渡します。isSelectedここで、新しく選択したファミリにアクセスし、そのプロパティを true に設定できます。ただし、サブスクリプションには、既定で呼び出される2 つの組み込みトピックがあります。1 つは先ほど説明した「変更」トピックで、もう 1 つは「beforeChange」です。isSelectedこれは、値が変更される前に呼び出され、フラグを false に設定するなど、値が変更される前にビジネス ロジックを実行する機会を提供します。

----------

この次の部分は、私が経験したいくつかの考慮事項と追加情報にすぎないことに注意してください。上記は適切な解決策ですが、もっと知りたい人は読み進めてください...

----------

別のアプローチは、selectedFamily次のようなプライベートな監視可能なバッキングで計算された読み取り/書き込みを作成することです。

var _selectedFamily = ko.observable();
self.selectedFamily = ko.computed({
    read: _selectedFamily,
    write: function (newValue) {
        var oldValue = _selectedFamily();
        if (oldValue) {
            oldValue.isSelected(false);
        }
        if (newValue) {
           newValue.isSelected(true);
        }
        _selectedFamily(newValue);
    }
});

私がこの設定をどのように行っているかは完全な証明ではありませんが、ここで概念のアイデアを提供する必要があります. これは悪いアプローチではなく、計算結果の設定の結果を決定する現在および次の値に基づいて決定を下す必要がある場合に使用するアプローチです。しかし、この状況ではisSelected、結果の値に関係がないため、ここにフラグを設定するのは好きではありません。設定された値の結果を決定する計算されたハウス ロジックを作成する必要があると仮定すると、前述のようにサブスクリプション ソリューションを選択しますが、非公開でバックアップされた_selectedFamilyオブザーバブルを選択します。計算されたものを経由せずに、プライベートにバックオブザーバブルの値を設定する必要があります。


コードで取り上げたもう 1 つの部分は、ボタンのクリック バインディングです。それらを束ねて包みましたwith。関数にバインドされたバインディングには、デフォルトで現在のコンテキストのオブジェクトが渡されます。これを行う代わりに:

self.addChild = function () {
    var selectedFamily = self.selectedFamily();
    // ...code...
};

できるよ:

self.addChild = function (selectedFamily) {
};

また、<span data-bind="visible: isSelected">| Selected!</span>バインディングを子供のコンテキストから家族のコンテキストに移動しました。これは意図されたものだと思います。「|」があるのは奇妙に思えました。姓ではなく、各子供の横に isSelected というテキストが表示されますが、子供ごとに表示したい場合は、次のようにします。

<li data-bind="text: fullName"><span data-bind="visible: $parent.isSelected">| Selected!</span></li>

KOforeachバインディングに関する最後の機能は、data/as プロパティを持つオブジェクトとのバインディングです。これにより、マークアップが人間にとって読みやすくなり、親チェーンへのアクセスが大幅に改善されます。isSelected家族の旗を使用して、子供の名前で何かを表示したいとしましょう。上記では、 を使用して参照する必要があります$parent。状況によっては、複数のネストされたバインディングがあり、参照しようとしているコンテキストに関する情報がほとんどないforeachようなものを書いていることに気付くかもしれません。<span data-bind="text: $parents[2].name"></span>このメソッドを使用すると、代わりに次のように書くことができます。

<div data-bind="foreach: { data: $root.families, as: 'family' }">
    <ul data-bind="foreach: { data: $family.children, as: 'child }>
        <li>
            <span data-bind="text: child.firstName">
            <span data-bind="text: family.name">
        </li>
    </ul>
</div>

このようにすれば、first と lastName を書き出すのに計算結果さえ必要ありません。複雑なネストされたコンテキスト バインディング構造を使用する予定がある場合や、単純なネストされた構造をより人間が読めるように見せたい場合に、この方法を使用します。

http://jsfiddle.net/34ZjB/1/

于 2013-09-14T19:30:12.453 に答える