Ember JS 中嵌套路由的动态出口
Dynamic Outlet for Nested Route in Ember JS
我想在 Ember 中创建一个类似于 Netflix 的界面,其中点击电影标题后,内容会显示在海报图像下方。我的想法是创建一个带
的嵌套路由
Router.map(function() {
this.route('movies', {path: '/' }, function() {
this.route('movie', { path: '/:id'});
});
});
问题是我不知道如何在每个电影组件中有一个 {{outlet}}
并告诉嵌套路由在相应的组件中渲染。有没有人用嵌套路由创建了类似的东西?
根据您是不关心 url、想要查询参数,还是希望 url 成为一条完整的路线,您可以选择不同的方向。
方法一:无URL状态
将movie-preview
和movie-detail
转化为组件。
您可以在这里使用 focus
来发挥您的优势。这样一来,select进行不同的电影预览将取消关注一个组件并关注另一个组件,而无需更新任何绑定。
电影中-preview.js
export default Component.extend({
attributeBindings: ['tabindex'],
tabindex: 0,
isFocused: false,
movie: undefined,
focusIn() {
this.set('isFocused', true);
},
focusOut() {
this.set('isFocused', false);
}
});
在电影预览的模板中,我们添加了一个条件。
{{#if isFocused}}
{{movie-detail movie=movie}}
{{/if}}
不过,也许我们要将这种状态反映到URL?让我们从如何为查询参数执行此操作开始。
方法二:查询参数
在路由的控制器上,为 selected
添加一个 query param。
在这条路线的模板中,我们将使用 eq
(一个帮助程序,一种可能的实现方式是 here)根据该路线的电影是否设置 isFocused
预览是当前 selected 的。我们还传递了一个设置焦点的动作。
{{#each movies as |movie|}}
{{电影预览
电影=电影
isFocused=(eq movie.id selected)
select=(行动"selectMovie" movie.id)
}}
{{/每个}}
movie-preview.js
将更改为在点击时触发 select。
export default Component.extend({
isFocused: false,
movie: undefined,
select: undefined,
click() {
this.sendAction('select');
}
});
最后,我们需要在该路由的控制器中处理此操作。
export default Controller.extend({
queryParams: ['selected'],
selected: undefined,
actions: {
selectMovie(movieId) {
this.set('selected', movieId);
}
}
});
但也许我们希望它有一个漂亮的 url?这也很简单。
方法三:漂亮URLs
第一步是设置嵌套路由,如您所示。
this.route('movies', {path: '/' }, function() {
this.route('movie-detail', { path: '/:id'});
});
为此,我们确实需要一个 outlet
正如您最初建议的那样,但我们不需要对方法 2 中的代码进行那么多更改。而不是 movie-detail
组件,我们现在有一个电影细节路线。因为我们之前不需要知道这方面的细节,所以我现在也跳过它。我们只关心 "activating" 路线的机制。
在电影预览模板中,我们将条件更改为包装插座。
{{#if isFocused}}
{{outlet}}
{{/if}}
在控制器中,我们需要触发一个转换,而不是修改查询参数。
export default Controller.extend({
selected: undefined,
actions: {
selectMovie(movieId) {
this.set('selected', movieId);
this.transitionToRoute('movies.movie-detail', movieId);
},
clearSelection() {
this.set('selected', undefined);
this.transitionToRoute('movies');
}
}
});
我想在 Ember 中创建一个类似于 Netflix 的界面,其中点击电影标题后,内容会显示在海报图像下方。我的想法是创建一个带
的嵌套路由Router.map(function() {
this.route('movies', {path: '/' }, function() {
this.route('movie', { path: '/:id'});
});
});
问题是我不知道如何在每个电影组件中有一个 {{outlet}}
并告诉嵌套路由在相应的组件中渲染。有没有人用嵌套路由创建了类似的东西?
根据您是不关心 url、想要查询参数,还是希望 url 成为一条完整的路线,您可以选择不同的方向。
方法一:无URL状态
将movie-preview
和movie-detail
转化为组件。
您可以在这里使用 focus
来发挥您的优势。这样一来,select进行不同的电影预览将取消关注一个组件并关注另一个组件,而无需更新任何绑定。
电影中-preview.js
export default Component.extend({
attributeBindings: ['tabindex'],
tabindex: 0,
isFocused: false,
movie: undefined,
focusIn() {
this.set('isFocused', true);
},
focusOut() {
this.set('isFocused', false);
}
});
在电影预览的模板中,我们添加了一个条件。
{{#if isFocused}}
{{movie-detail movie=movie}}
{{/if}}
不过,也许我们要将这种状态反映到URL?让我们从如何为查询参数执行此操作开始。
方法二:查询参数
在路由的控制器上,为 selected
添加一个 query param。
在这条路线的模板中,我们将使用 eq
(一个帮助程序,一种可能的实现方式是 here)根据该路线的电影是否设置 isFocused
预览是当前 selected 的。我们还传递了一个设置焦点的动作。
{{#each movies as |movie|}} {{电影预览 电影=电影 isFocused=(eq movie.id selected) select=(行动"selectMovie" movie.id) }} {{/每个}}
movie-preview.js
将更改为在点击时触发 select。
export default Component.extend({
isFocused: false,
movie: undefined,
select: undefined,
click() {
this.sendAction('select');
}
});
最后,我们需要在该路由的控制器中处理此操作。
export default Controller.extend({
queryParams: ['selected'],
selected: undefined,
actions: {
selectMovie(movieId) {
this.set('selected', movieId);
}
}
});
但也许我们希望它有一个漂亮的 url?这也很简单。
方法三:漂亮URLs
第一步是设置嵌套路由,如您所示。
this.route('movies', {path: '/' }, function() {
this.route('movie-detail', { path: '/:id'});
});
为此,我们确实需要一个 outlet
正如您最初建议的那样,但我们不需要对方法 2 中的代码进行那么多更改。而不是 movie-detail
组件,我们现在有一个电影细节路线。因为我们之前不需要知道这方面的细节,所以我现在也跳过它。我们只关心 "activating" 路线的机制。
在电影预览模板中,我们将条件更改为包装插座。
{{#if isFocused}}
{{outlet}}
{{/if}}
在控制器中,我们需要触发一个转换,而不是修改查询参数。
export default Controller.extend({
selected: undefined,
actions: {
selectMovie(movieId) {
this.set('selected', movieId);
this.transitionToRoute('movies.movie-detail', movieId);
},
clearSelection() {
this.set('selected', undefined);
this.transitionToRoute('movies');
}
}
});