2

値だけでなく、ノックアウト テンプレートから観察可能なデータ コンテキスト全体にアクセスする必要があります。

私が開発しているアプリケーションでは、多くの場合、ビューを一般的にレンダリングするために使用する多くのメタ データがあります。過去に、ビュー モデルのプロパティを複雑にして、メタ データとデータの両方をサブ プロパティとして (値プロパティに値を含めて) 格納しました。

ViewModel.AwesomeProperty = {
    value: ko.observable('Awesome Value'),
    label: 'My Awesome Label',
    template: 'awesomeTemplate',
    otherMetaData: 'etc'
}

私はこのメタ データを変更して、オブザーバブルのプロパティにします (Ryan Niemeyer が彼のブログ投稿またはセッションで説明したと思います)。私は、それがよりクリーンで、よりエレガントで、一般的により保守しやすく、オーバーヘッドが少ないことがわかりました (特にシリアライゼーションに関しては)。上記の例と同等のものは次のようになります。

ViewModel.AwesomeProperty = ko.observable('Awesome Value');
ViewModel.AwesomeProperty.label = 'My Awesome Label';
ViewModel.AwesomeProperty.template = 'awesomeTemplate';
ViewModel.AwesomeProperty.otherMetaData = 'etc';

これを行うことの副作用は、ViewModel.AwesomeProperty をテンプレートに渡すと、データ コンテキストがオブザーバブルの値 (この場合は「Awesome Value」) に設定され、$data からメタデータにアクセスできなくなることです。

<script id="example" type="text/html">
    <!-- This won't work anymore -->
    <span data-bind="text: $data.label></span>
</script>
<div data-bind="template: {name: 'example', data: AwesomeProperty}"></div>

私が今持っている回避策は、次のように匿名オブジェクトでデータ値をラップすることです:

<script id="example" type="text/html">
    <!-- Now it works again -->
    <span data-bind="text: data.label></span>
</script>
<div data-bind="template: {name: 'example', data: {data:AwesomeProperty}}"></div>

しかし、これはエレガントではなく、理想的ではありません。自動生成が多い場合、これは不便であるだけでなく、実際には大きな障害となります。テンプレート バインディングをラップするカスタム バインディングを作成することを検討しましたが、より良い解決策があることを願っています。

これは、ドロップダウンのカスケードに取り組んできた実際の例です。この JSFiddleは機能しますが、その JSFiddleは機能しません。

前もって感謝します。

4

1 に答える 1

1

問題は、ノックアウトがアンラップ後に常に値を使用することです。それがオブザーバブルである場合、それらのサブプロパティは失われます。オブザーバブルを別のオブジェクトにラップし直して、すでに見つけたように失わないようにする必要があります。

これをラップする良い方法は、この再ラップを行うサブスクライブ可能 (または派生型のいずれか) 用の関数を作成することです。この再ラップされたオブジェクトにすべての個々のメタデータを追加するか、それらを独自の個別のオブジェクトにパックすることができます。コードが再びエレガントになります。

var buildSelection = function (choices, Parent) {
    return _(ko.observable()).extend({
        // add the metadata to a 'meta' object
        meta: {
            choices: choices,
            availableChoices: ko.computed(function () {
                if (!Parent) return choices;
                if (!Parent()) return [];
                return _(choices).where({ ParentID: Parent().ID });
            })
        }
    });
}

ko.subscribable.fn.templateData = function (metaName) {
    return {
        // access the value through 'value'
        value: this,
        // access the metadata through 'meta'
        meta: this[metaName || 'meta'] // meta property may be overridden
    };
}

次に、バインディングでこの関数を呼び出して、再ラップされたオブジェクトを作成します。テンプレートでバインドを調整することを忘れないでください。

<script id="Selection" type="text/html">
    <select data-bind="
            options: meta.availableChoices,
            optionsText: 'Value',
            value: value,
            optionsCaption: 'Select One',
            enable: meta.availableChoices().length
    "></select>
</script>

<!-- ko template: { 'name': 'Selection', 'data': Level1.templateData() } --><!-- /ko -->
<!-- ko template: { 'name': 'Selection', 'data': Level2.templateData() } --><!-- /ko -->
<!-- ko template: { 'name': 'Selection', 'data': Level3.templateData() } --><!-- /ko -->

更新されたフィドル

于 2012-11-07T04:27:09.177 に答える