Ember.js - 如何在嵌套/重复视图中定位出口,以及这种ui布局的最佳实践是什么?

Gop*_*han 9 javascript controller outlet handlebars.js ember.js

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

看起来网点是最好的方法.

现在,我有一个显示许多元素的UI,每个元素都使用模板化视图进行渲染.

{{#each item in controller}}
      {{view App.ItemThumbView}}
{{/each}}
Run Code Online (Sandbox Code Playgroud)

此视图的右侧边栏是根据选择而更改的插座.当我选择一个项目时,我想在模板化的子视图中显示一个编辑操作列表,选中后,通过嵌套的插座显示正确的编辑UI.

实质上

+---------------------------------------------------+
|        Parent Pane
|
| +------------------------------+ +----------+
| |  Device Section              | | Sidebar  |
| |                              | | [Outlet] |
| |  +--------+ +---------+      | |          |
| |  | Dev 1  | |  Dev 2  |      | |          |
| |  |[outlet]| | [outlet]|      | +----------+
| |  +--------+ +---------+      |
| +------------------------------+ 
+--------------------------------------------------+
Run Code Online (Sandbox Code Playgroud)

嵌套视图都共享相同的控制器 - 这是有道理的 - 但我需要能够将选定的视图连接到其相应的插座.我最初尝试连接插座时从未显示过.代码根本没有失败,因此控制器只是更新隐藏的插座.

如何在Ember中为嵌套视图定位正确的插座?(最好的情况是,我似乎能够触及侧栏插座,但不能触及嵌套设备模板中的插座.)这是否是首先在ember中实现上下文菜单的合理结构?

*澄清使用我当前的设置,每个设备项目使用相同的模板呈现.选中后,侧栏插座将使用某些设备的元信息进行更新,而所选设备视图也会将其插座连接到编辑菜单.一次只能连接一个设备项目的"编辑"插座.

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

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

Outlets似乎非常适合解耦组件,以及面向未来的潜在视图嵌套.但是现在看来,访问嵌套视图的正确插座有点麻烦.此外,如果您始终知道哪些组件将有条件地嵌套在模板中,则最简单的方法是对视图进行硬编码.例如:

// within template used for individual result-list items
{{#if condition }}
   {{view reusableSubView}}
{{/if} 
Run Code Online (Sandbox Code Playgroud)

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

Gop*_*han 13

好吧,经过数小时和数小时的游戏后,最好有条件地在我的嵌套视图正上方的一个级别中包含一个命名插座.

这是因为您需要一个可靠的插座来定位模板,并且似乎没有一种很好的方法可以在运行时以编程方式命名插座.

此外,您希望定位的出口需要存在于路由器,控制器和用于呈现屏幕当前状态的呈现模板的嵌套中的父模板内的某个位置.如果未通过父路径对象放置插座,则无法将其定位在叶节点路径中.

让我用一个例子来解释一下:

我最初问过我的问题,我们的用户界面已从设备列表变为人员列表,但原则保持不变.

我有一张带有照片的人员列表,可以点击它们在结果旁边显示更多信息.

我有一个人员模板,看起来像:

<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>
Run Code Online (Sandbox Code Playgroud)

一个人模板有点像这样:

<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>
Run Code Online (Sandbox Code Playgroud)

以及带有额外信息和编辑选项的详细信息模板

<script type="text/x-handlebars" data-template-name="person/details" > 
various edit options    
</script>
Run Code Online (Sandbox Code Playgroud)

在单击人员结果时,我的路由器将获取网址更新,并将详细信息模板中的存根添加到父人模板中.我的路由器设置如下:

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

});
Run Code Online (Sandbox Code Playgroud)

随着个别路线:

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!");
    }
});
Run Code Online (Sandbox Code Playgroud)

我最初试图在PersonView中设置插座,但这不起作用,因为我的人员模板实际上是在人行道中呈现的.当应用程序到达人员路线时,控制器已经呈现了列表中的所有人.我想要定位的个人很久以前就已经过去了.

通过让路由充当父模板和期望的子模板之间的中介,我能够实现这一点.

最后,关于是否在模板中明确声明嵌套视图或仅使用插座的问题,我相信网点更清晰.虽然这个解决方案涉及到一些解决方案,但是过于复杂的模板对象更为可取.现在我希望能够实现任意复杂的模板嵌套,而不会触及除渲染中涉及的路径之外的任何内容.

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