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

2023-12-28

我正在重构一个继承的 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 中实现上下文菜单来说是一个合理的结构吗?

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

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

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

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

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

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


好吧,经过几个小时的研究,似乎最好有条件地在嵌套视图正上方的级别中包含一个命名出口。

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

此外,您希望定位的插座需要存在于父模板中的某个位置,该父模板位于路由器、控制器和用于呈现屏幕当前状态的呈现模板的嵌套中。如果父路由对象尚未放置出口,则您不能希望将其定位到叶节点路由中。

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

自从我最初提出问题以来,我们的 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 中拥有出口,但这行不通,因为我的人员模板实际上是在人员路线中呈现的。当应用程序到达人员路线时,控制器已经渲染了列表中的所有人员。我想要瞄准的那个人早已过去了。

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

最后,关于是否在模板中显式声明嵌套视图,或仅使用出口的问题,我认为出口要干净得多。虽然此解决方案涉及一些围绕路线的工作,但它比拥有过于复杂的模板对象要好得多。现在,我希望能够实现任意复杂的模板嵌套,而无需触及除渲染中涉及的路线之外的任何内容。

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Ember.js——如何在嵌套/重复视图中定位插座,以及这种 ui 布局的最佳实践是什么? 的相关文章

随机推荐