9

私は、継承された Ember アプリケーションのリファクタリングに取り組んでいますが、mvc 以外の障害がかなりあります。私は物事を可能な限りモジュール化することを目指しており、コードの重複を防ぐために複数の画面でさまざまな UI コンポーネントを再利用したいと考えています。

これを行うには、アウトレットが最適な方法のようです。

現在、多数の要素を表示する UI があり、それぞれがテンプレート化されたビューを使用してレンダリングされています。

{{#each item in controller}}
      {{view App.ItemThumbView}}
{{/each}}

このビューの右側のサイドバーは、選択に基づいて変化するアウトレットです。項目を選択すると、テンプレート化されたサブビュー内に編集操作のリストを表示したいと考えています。これを選択すると、ネストされたアウトレットを通じて適切な編集 UI が表示されます。

本質的に

+---------------------------------------------------+
|        Parent Pane
|
| +------------------------------+ +----------+
| |  Device Section              | | Sidebar  |
| |                              | | [Outlet] |
| |  +--------+ +---------+      | |          |
| |  | Dev 1  | |  Dev 2  |      | |          |
| |  |[outlet]| | [outlet]|      | +----------+
| |  +--------+ +---------+      |
| +------------------------------+ 
+--------------------------------------------------+

ネストされたビューはすべて同じコントローラーを共有しますが (これは理にかなっています)、選択したビューを対応するアウトレットに接続できるようにする必要があります。コンセントを接続する最初の試みが表示されません。コードはまったく失敗しないため、コントローラーは非表示のアウトレットを更新しているだけです。

Ember でネストされたビューの正しいアウトレットをターゲットにするにはどうすればよいですか? (せいぜい、サイドバーのアウトレットにはヒットできるようですが、ネストされたデバイス テンプレート内のアウトレットにはヒットできないようです。) そして、これはそもそも ember でコンテキスト メニューを実装するための合理的な構造ですか?

*明確にするために、現在のセットアップでは、各デバイス アイテムは同じテンプレートを使用してレンダリングされます。選択すると、サイドバー アウトレットが一部のデバイスのメタ情報で更新され、選択されたデバイス ビューもそのアウトレットを編集メニューに接続します。一度に 1 つのデバイス項目だけが「編集」アウトレットに接続されます。

ここでアウトレットを使用することは理にかなっていますか?それとも、必要に応じて編集メニューを表示するために条件付きロジックをテンプレートにドロップする必要がありますか?

更新して、質問のベスト プラクティスの部分を言い換えます

アウトレットは、コンポーネントのデカップリング、および潜在的なビューのネストの将来性を保証するのに最適なようです。しかし、現時点では、ネストされたビューの正しいアウトレットにアクセスするのは少し面倒です。さらに、テンプレート内で条件付きでネストされるコンポーネントが常にわかっている場合は、ビューをハードコーディングするのが最も簡単なようです。例えば:

// within template used for individual result-list items
{{#if condition }}
   {{view reusableSubView}}
{{/if} 

ここで物事を行うための好ましいエンバーの方法は何ですか? 常に接続されているとは限らないコンセントを作成することでオーバーヘッドは発生しますか?

4

1 に答える 1

13

さて、これで何時間も遊んだ後、ネストされたビューのすぐ上のレベルに条件付きで名前付きアウトレットを含めるのが最善のようです。

これは、テンプレート内で対象とする単一の信頼できるアウトレットが必要であり、実行時にプログラムでアウトレットに名前を付ける良い方法がないように思われるためです。

さらに、ターゲットにするアウトレットは、ルーター、コントローラー、および画面の現在の状態をレンダリングするために使用されるレンダリングされたテンプレートのネスト内の親テンプレート内のどこかに存在する必要があります。親ルート オブジェクトによってアウトレットが配置されていない場合、リーフ ノード ルートでそれをターゲットにすることは期待できません。

例を挙げて説明しましょう:

最初に質問したときから、UI はデバイスのリストから人のリストに変更されましたが、原則は同じままです。

写真付きの人のリストがあり、それらをクリックすると、結果の横に詳細情報が表示されます。

次のような人物テンプレートがあります。

<script type="text/x-handlebars" data-template-name="people" >
    <h3>People in this group</h3>
    <div class="peeps">
        {{#each controller}}
             {{ view App.PersonView }}
             {{#if selected }}
                   <div class='too-personal'>
                   {{ outlet veryPersonalDetails }}
                   </div>
             {{/if}}
         {{/each}} 
    </div>
</script>

そして、このような人のテンプレート:

<script type="text/x-handlebars" data-template-name="person" >
        {{#linkTo person this}}
            <div data-desc="person-item" class="item">
                <div class="portrait">
                    <img class="avatar" {{bindAttr src="gravatarUrl"}}/>
                </div>
                <div class="statuslabel"><span {{bindAttr class=":label statusLabel"}}>{{statusText}}</span></div>
                <div class="cTitle">{{fullName}}</div>
            </div>
        {{/linkTo}}     </script>

追加情報と編集オプションを含む詳細テンプレート

<script type="text/x-handlebars" data-template-name="person/details" > 
various edit options    
</script>

個人の結果をクリックすると、ルーターが URL の更新を取得し、詳細テンプレートを親の個人テンプレートにスタブします。私のルーターは次のように設定されています:

App.Router.map(function() {
...
    this.resource('people', { path: '/people'}, function() {
        this.resource('person', { path: '/:person_id' },
            function() {
                this.route('details');
            }
        );
    });

});

個々のルートの場合:

App.PeopleRoute = Ember.Route.extend({
    model: function() {
        return App.People.find();
    },
    setupController: function(controller, model) {
        controller.set('content', model);
    }
});

App.PersonRoute = Ember.Route.extend({
    model: function(params) {
        return App.People.peepsById[params.person_id];
    },

    setupController: function(controller, model) {
        this._super(controller, model);
        var person = model;
// this block ensures that only one person has a selected status at a time
// ignore the overly-verbose code. I'm cleaning it up tomorrow 
        var ls = App.People.lastSelected;
        if (ls) {
            ls.set('selected', false);
        }
        person.set('selected', true);
        App.People.lastSelected = person;
        controller.set('content', model);
    },

    renderTemplate: function() {
        // person is actually stored in router 'context' rather than 'model'
        // omg!
        var x = this.controllerFor('personDetails').set('content', this.get('context'));
        this.render('person/details', { // render person/details
            into:'people', // into the people template
            outlet: "veryPersonalDetails", // at the veryPersonalDetails outlet
            controller: x // using the personDetails controller for rendering
        });

// additional rendering in sidebar outlet for testing
        this.render('person/details',{
            into:'people',
            outlet: "personDetails",
            controller: x
        });

        console.log("@@@ we did it!");
    }
});

私は元々、PersonView 内にアウトレットを持たせようとしましたが、私の個人テンプレートは実際には People ルート内でレンダリングされたため、これは機能しませんでした。アプリケーションが人のルートに到達するまでに、コントローラーはリスト内のすべての人を既にレンダリングしています。私がターゲットにしたい個人はずっと前に通り過ぎました。

親テンプレートと必要な子テンプレートの間の仲介役としてルートを機能させることで、これを実現することができました。

最後に、ネストされたビューをテンプレート内で明示的に宣言するか、それとも単にアウトレットを使用するかという問題に関しては、アウトレットの方がはるかにクリーンだと思います。このソリューションには、ルートを回避するための作業が少し含まれていましたが、過度に複雑なテンプレート オブジェクトを使用するよりははるかに望ましい方法です。これで、レンダリングに関係するルート以外には何も触れずに、テンプレートの任意の複雑なネストを実現できることを願っています。

さらに、このソリューションは、未使用のコンセントのオーバーヘッドを排除します。「ドム」にたくさんの空のコンテナ オブジェクトを散らかすのではなく、使用する予定のアウトレットのみを使用する必要があると思います。

于 2013-01-18T23:12:42.877 に答える