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-previewmovie-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');
    }
  }
});