Ember.js - Как мне настроить таргетинг выходов во вложенных / повторных представлениях и каковы лучшие практики для такого макета пользовательского интерфейса?
Я работаю над рефакторингом унаследованного приложения Ember, с немалым беспорядком, не связанным с mvc. Я стремлюсь сделать все как можно более модульным и надеюсь повторно использовать различные компоненты пользовательского интерфейса на нескольких экранах, чтобы предотвратить дублирование кода.
Кажется, торговые точки - лучший способ сделать это.
Прямо сейчас у меня есть пользовательский интерфейс, отображающий несколько элементов, каждый из которых отображается с использованием шаблонного представления.
{{#each item in controller}}
{{view App.ItemThumbView}}
{{/each}}
Правая боковая панель этого вида представляет собой выход, который изменяется в зависимости от выбора. Когда я выбираю элемент, я хотел бы отобразить список операций редактирования в шаблонном вложенном представлении, который при выборе отображает правильный пользовательский интерфейс редактирования через вложенную розетку.
по существу
+---------------------------------------------------+
| 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}
Каков предпочтительный угольный способ делать вещи здесь? Есть ли какие-либо накладные расходы на создание торговых точек, которые не всегда могут быть подключены постоянно?
1 ответ
Хорошо, после нескольких часов игры с этим, лучше всего условно включить именованный выход в уровень прямо над моим вложенным представлением.
Это потому, что вам нужен один надежный выход для цели в шаблоне, и, похоже, нет хорошего способа программно назвать имя выхода во время выполнения.
Кроме того, выход, на который вы надеетесь нацелиться, должен существовать где-то в родительском шаблоне во вложении маршрутизаторов, контроллеров и шаблонов визуализации, используемых для визуализации текущего состояния экрана. Если выход не был установлен родительским объектом маршрута, вы не можете надеяться нацелиться на него в своем маршруте конечного узла.
Позвольте мне объяснить на примере:
Поскольку изначально я задавал свой вопрос, наш пользовательский интерфейс изменился со списка устройств на список людей, но принцип остается тем же.
У меня есть список людей с их фотографиями, и их можно щелкнуть, чтобы показать дополнительную информацию рядом с их результатом.
У меня есть шаблон людей, который выглядит примерно так:
<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, но это не сработало, так как мой личный шаблон фактически отображался в маршруте людей. К тому времени, когда приложение достигает маршрута человека, контроллер уже отобразил всех людей в списке. Человек, на которого я хочу нацелиться, давно прошел мимо.
Благодаря тому, что маршрут выступил в качестве посредника между родительским и желаемым дочерними шаблонами, я смог осуществить это.
Наконец, что касается вопроса о том, следует ли явно объявлять вложенные представления в шаблонах или просто использовать торговые точки, я считаю, что торговые точки намного чище. В то время как это решение включало в себя небольшую работу с маршрутами, гораздо предпочтительнее иметь слишком сложные объекты-шаблоны. Теперь я надеюсь, что я могу достичь сколь угодно сложной вложенности шаблонов, не затрагивая ничего, кроме маршрутов, участвующих в их визуализации.
Кроме того, это решение устраняет накладные расходы неиспользованных торговых точек. Я считаю, что у вас должны быть только те розетки, которые вы собираетесь использовать, а не засорять "дом" кучей пустых контейнерных объектов.