9

我正在重构一个继承的 Ember 应用程序,它带有相当多的非 mvc 混乱。我希望尽可能保持模块化,并希望在多个屏幕中重用各种 ui 组件以帮助防止代码重复。

似乎网点是做到这一点的最佳方式。

现在,我有一个 UI 显示了许多元素,每个元素都使用模板化视图呈现。

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

此视图的右侧边栏是一个出口,它会根据选择而变化。When I select an item, I would like to display a list of edit operations within the templatized sub-view, which when selected, reveal the proper edit UI through a nested outlet.

本质上

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

嵌套视图都共享同一个控制器——这是有道理的——但我需要能够将选定的视图连接到其相应的插座。我最初尝试连接插座的尝试从未显示出来。代码根本没有失败,所以控制器只是更新了一个隐藏的插座。

如何在 Ember 中为嵌套视图定位正确的出口?(充其量,我似乎能够点击侧边栏插座,但不能点击嵌套设备模板中的插座。)这是首先在 ember 中实现上下文菜单的合理结构吗?

*澄清一下 在我当前的设置中,每个设备项都使用相同的模板呈现。选中后,侧边栏插座将使用某些设备的元信息进行更新,而选定的设备视图还将其插座连接到编辑菜单。一次只能连接一个设备项目的“编辑”插座。

甚至在这里使用插座是否有意义,或者我应该将条件逻辑放入模板中以便在必要时显示编辑菜单?

更新以重申问题的最佳实践部分

插座似乎非常适合解耦组件和面向未来的潜在视图嵌套。但是现在似乎访问嵌套视图的正确出口有点麻烦。此外,如果您总是知道哪些组件将有条件地嵌套在模板中,那么对您的视图进行硬编码似乎是最简单的。例如:

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

这里首选的 ember 做事方式是什么?创建不一定始终连接的插座是否有任何开销?

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 中使用插座,但这不起作用,因为我的人员模板实际上是在人员路线中呈现的。当应用程序到达人员路由时,控制器已经渲染了列表中的所有人员。我要定位的那个人早就过去了。

通过让路由充当父模板和所需子模板之间的中介,我能够做到这一点。

最后,关于是在模板中显式声明嵌套视图还是只使用 outlet 的问题,我认为 outlet 更干净。虽然这个解决方案涉及到一些围绕路线的工作,但它比拥有过于复杂的模板对象要好得多。现在,我希望能够实现任意复杂的模板嵌套,而无需触及它们渲染中涉及的路由之外的任何东西。

此外,该解决方案消除了未使用插座的开销。我相信你应该只拥有你打算使用的插座,而不是用一堆空容器对象乱扔“dom”。

于 2013-01-18T23:12:42.877 回答