Ember.js - 如何在嵌套/重复视图中定位插座,以及这种ui布局的最佳做法是什么? [英] Ember.js -- How do I target outlets in nested/repeated views, and what are the best practices for such a ui layout?

查看:122
本文介绍了Ember.js - 如何在嵌套/重复视图中定位插座,以及这种ui布局的最佳做法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



看起来出口是最好的方法来做到这一点。



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

  {{#控制器中的每个项目}} 
{{View App.ItemThumbView}}
{ {/ each}}

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



基本上

  + ---------------------- ----------------------------- + 
|父窗格
|
| + ------------------------------ + + ---------- +
| |设备部分| |侧栏|
| | | | [Outlet] |
| | + -------- + + --------- + | | |
| | | Dev 1 | | Dev 2 | | | |
| | | [outlet] | | [outlet] | | + ---------- +
| | + -------- + + --------- + |
| + ------------------------------ +
+ ------------- ------------------------------------- +

嵌套视图共享同一个控制器 - 这是有意义的 - 但我需要能够连接一个选定的视图到其相应的插座。我最初尝试连接插座从不显示。代码不会失败,所以控制器只是更新一个隐藏的插座。



如何在Ember中为嵌套视图定位正确的插座? (最好,我似乎能够打开侧边栏出口,但不是嵌套设备模板中的插座。)这是一个合理的结构,用于在ember中实现上下文菜单首先?



* 澄清
根据目前的设定,每个装置项目都使用相同的范本呈现。当选择时,侧边栏出口将更新一些设备的元信息,而所选择的设备视图也将其出口连接到编辑菜单。每次只能有一个设备项目的编辑出口连接。



在这里甚至使用插座有意义,或者我应该将条件逻辑

$

b
$ b

出口对于解耦组件以及面向未来的潜在视图嵌套非常有用。但是现在看来,为嵌套视图访问正确的插座是一个有点麻烦。此外,如果你总是知道什么组件将有条件地嵌套在模板中,似乎最容易只是硬编码你的视图。例如:

  //在用于单个结果列表项的模板中
{{#if condition}}
{{view reusableSubView}}
{{/ if}

ember这里做事的方式?创建可能不一定在任何时候都连接的插座有任何开销吗?

解决方案

好的,经过几个小时和几个小时的玩弄,似乎最好有条件地包括一个命名的出口在我的嵌套视图之上。



这是因为你需要一个单一的,可靠的插件来定位一个模板,似乎没有一个很好的方式来编程名称出口在运行时。



此外,您希望定位的插座需要存在于父模板中的某处,嵌套在路由器,控制器和渲染模板中,用于呈现当前状态屏幕。如果一个父路由对象没有插入一个插座,你不能希望在你的叶节点路由中定位它。



让我以一个例子:



SInce我最初问我的问题,我们的UI已经从设备列表更改为人员列表,但原则保持不变。



我有一张有照片的人的名单,他们可以点击,在他们的结果旁边显示一些更多的信息。



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

 < script type =text / x-handlebarsdata-template- name =people> 
< h3>此群组中的人< / 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-handlebarsdata-template-name =person> 
{{#linkTo person this}}
< div data-desc =person-itemclass =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-handlebarsdata-template-name =person / details> 
各种编辑选项
< / 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;
//每次只有一个人有一个选定的状态
//忽略过多冗长的代码我明天清理
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实际上存储在路由器而不是'model'
// omg!
var x = this.controllerFor('personDetails')。set('content',this.get('context'));
.render('person / details',{// render person / details
into:'people',// into the people template
outlet:veryPersonalDetails,//在veryPersonalDetails outlet
controller:x //使用personDetails控制器渲染
});

//用于测试的侧边栏输出中的附加渲染
this.render('person / details',{
into:'people',
outlet:personDetails ,
controller:x
});

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

我最初尝试在PersonView中插座,但这不会工作,模板实际上在人员路线中呈现。在应用程序到达人员路由时,控制器已经呈现列表中的所有人员。我想要瞄准的个人很久以前就已经过去了。



通过将路由作为父级模板和所需子模板之间的中介,我能够实现这一点。



最后,关于是在模板中显式声明嵌套视图还是只使用插座,我相信插座更干净。虽然这个解决方案涉及了一些在路由上工作,但是最好是过于复杂的模板对象。现在我可以希望实现任意复杂的模板嵌套,而不涉及任何东西,但涉及到它们的渲染路线。



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


I'm working on refactoring an inherited Ember application, with quite a bit of non-mvc disorder to it. I'm looking to keep things as modular as possible, and am hoping to reuse various ui components in multiple screens to help prevent code duplication.

It seems like outlets are the best way to do this.

Right now, I have a UI displaying a number of elements, each rendered using a templatized view.

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

The right sidebar of this view is an outlet that changes based on the selection. 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.

Essentially

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

The nested views all share the same controller -- which makes sense -- but I need to be able to connect a selected view to its corresponding outlet. My initial attempts at connecting outlets never display. The code doesn't fail at all, so the controller's just updating a hidden outlet.

How do I target the correct outlet for a nested view in Ember? (At best, I seem to be able to hit the sidebar outlet, but not the outlet within a nested device template.) And is this a reasonable structure for implementing contextual menus in ember in the first place?

* For clarification With my current setup, each device item is rendered using the same template. When selected, the sidebar outlet would update with some device's meta information, while the selected device view would also connect its outlet to an edit menu. Only one device item would have its 'edit' outlet connected at a time.

Does it make sense to even use an outlet here, or should I be dropping conditional logic into the template in order to display the edit menus as necessary?

Update to restate the best practices portion of the question:

Outlets seem to be great for decoupling components, and future-proofing potential view nestings. But right now it seems like accessing the correct outlet for a nested view is a bit cumbersome. Further, if you always know what component(s) will be conditionally nested within a template, it seems easiestto just hardcode your views. eg:

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

What is the preferred ember way of doing things here? Is there any overhead to creating outlets that may not necessarily be connected at all times?

解决方案

Okay, after hours and hours of playing around with this, it seems best to conditionally include a named outlet in a level directly above my nested view.

This is because you need a single, reliable outlet to target within a template, and there doesn't seem to be a nice way to programmatically name an outlet at runtime.

Further, the outlet you hope to target needs to exist somewhere within a parent template in the nesting of routers, controllers, and rendered templates used to render the current state of the screen. If an outlet hasn't been put in place by a parent route object, you cannot hope to target it in your leaf-node route.

Let me explain with an example:

SInce I originally asked my question, our UI has changed from a list of devices to a list of people, but the principle remains the same.

I have a list of people with their photos, and they can be clicked to show some more info next to their result.

I have a people template that looks something like:

<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>

And a person template that's kinda like this:

<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>

And the details template with the extra information and edit options

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

On clicking a person result, my router picks up the url update, and stubs in the details template into the parent people template. My router is set up something like this:

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

});

With the individual routes:

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!");
    }
});

I originally tried to have the outlet within the PersonView, but this wouldn't work, as my person template was actually rendered within the people route. By the time the application reaches the person route, the controller has already rendered all the people in the list. The individual person I want to target has been passed by long ago.

By having the route act as an intermediary between the parent and desired child templates, I was able to make this happen.

Finally, regarding the question of whether to explicitly declare nested views within templates, or to just use outlets, I believe outlets are far cleaner. While this solution involved a bit of working around the routes, it's far preferable to having overly complicated template objects. Now I can hopefully achieve arbitrarily complex nestings of templates without touching anything but the routes involved in their rendering.

Further, this solution eliminates the overhead of unused outlets. I believe you should only have outlets you intend to use, rather than littering the 'dom' with a bunch of empty container objects.

这篇关于Ember.js - 如何在嵌套/重复视图中定位插座,以及这种ui布局的最佳做法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆