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?

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

问题描述

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

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

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

{{#控制器中的每一项}}{{查看 App.ItemThumbView}}{{/每个}}

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

本质上

+---------------------------------------------------+|父面板||+------------------------------+ +--------------+||设备部分 ||侧边栏 |||||[奥特莱斯] |||+--------+ +---------+ ||||||开发 1 ||开发 2 |||||||[出口]||【出线】||+-----------+||+--------+ +---------+ ||+------------------------------++--------------------------------------------------+

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

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

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

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

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

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

//在用于单个结果列表项的模板中{{#if 条件}}{{查看 reusableSubView}}{{/如果}

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

解决方案

好的,经过几个小时的尝试,似乎最好在嵌套视图正上方的级别中有条件地包含一个命名插座.

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

此外,您希望定位的插座需要存在于父模板内的某个位置,该模板位于路由器、控制器和用于渲染屏幕当前状态的渲染模板的嵌套中.如果一个出口没有被父路由对象放置到位,你不能希望在你的叶节点路由中定位它.

让我用一个例子来解释:

自从我最初提出我的问题以来,我们的 UI 已从设备列表更改为人员列表,但原理保持不变.

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

我有一个类似于以下内容的人员模板:

还有一个类似于这样的人物模板:

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

App.Router.map(function() {...this.resource('people', { path: '/people'}, function() {this.resource('person', { path: '/:person_id' },功能() {this.route('详细信息');});});});

使用单独的路线:

App.PeopleRoute = Ember.Route.extend({模型:函数(){返回 App.People.find();},设置控制器:功能(控制器,模型){控制器.set('内容', 模型);}});App.PersonRoute = Ember.Route.extend({模型:函数(参数){返回 App.People.peepsById[params.person_id];},设置控制器:功能(控制器,模型){this._super(控制器,模型);var 人 = 模型;//此块确保一次只有一个人具有选定状态//忽略过于冗长的代码.我明天打扫var ls = App.People.lastSelected;如果(ls){ls.set('选中', false);}person.set('选择', true);App.People.lastSelected = 人;控制器.set('内容', 模型);},渲染模板:函数(){//person 实际上存储在路由器上下文"而不是模型"中//我的天啊!var x = this.controllerFor('personDetails').set('content', this.get('context'));this.render('person/details', {//渲染人/细节into:'people',//进入人员模板出口:veryPersonalDetails",//在veryPersonalDetails出口controller: x//使用 personDetails 控制器进行渲染});//在侧边栏出口中进行额外渲染以进行测试this.render('人物/细节',{成:'人',出口:personDetails",控制器:x});console.log("@@@ 我们做到了!");}});

我最初尝试在 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天全站免登陆