Clean Ember 1.13+ 了解子路由是否激活的方法

Clean Ember 1.13+ way of knowing if child route is activated

假设我们有一个 Article 模型如下:

export default DS.Model.extend({
  author: DS.belongsTo('user'),
  tagline: DS.attr('string'),
  body: DS.attr('string'),
});

还假设我们有很多页面,并且在每个页面上我们都想要一个显示全新文章标语的行情收录器。由于它在每个页面上,我们在 应用程序根 级别加载所有(新)文章,并让组件显示它们:

{{taglines-ticker articles=articles}}
{{output}}

这样我们就可以访问任何嵌套页面并查看标语(无需将组件添加到每个页面)。

问题是,我们不想在查看文章时看到文章的代码标语,但根级别taglines-ticker不知道激活了什么子路由,所以我们不能简单地按 params.article_id 进行过滤。有没有一种干净的方法可以将该信息传递给父路由?

注: 不是 how to determine active child route in Ember 2? 的副本,因为它不涉及显示 {{link-to}}

的活动链接

您仍然可以使用 link-to 组件来完成此操作,我认为这是一种简单的方法。您没有共享 taglines-ticker 模板,但在其中您必须为每篇文章提供某种列表。创建一个从 link-to 组件扩展而来的新 tagline-ticker 组件,然后使用它的 activeClasscurrent-when 属性在当前路由时隐藏标语。它不需要是 link,或者根本不需要看起来像 link。

tagline-ticker.js:

export default Ember.LinkComponent.extend({
  // div or whatever you want
  tagName: 'div',
  classNames: ['whatever-you-want'],
  // use CSS to make whatever class you put here 'display: none;'
  activeClass: 'hide-ticker',
  // calculate the particular route that should hide this tag in the template
  'current-when': Ember.computed(function() {
    return `articles/${this.get('article.id')}`;
  }),

  init() {
    this._super(arguments);
    // LinkComponents need a params array with at least one element
    this.attrs.params = ['articles.article'];
  },
});

tagline-ticker 用于 taglines-ticker.hbs:

{{#tagline-ticker}}
  Article name
{{/tagline-ticker}}

CSS:

.hide-ticker {
  display: none;
}

Ember 正在 2.15 中添加适当的 router 服务;这会公开有关当前路由的信息以及一些允许检查路由器状态的方法。在旧版本的 Ember 上有一个 polyfill,它可能适合你,具体取决于你当前使用的版本:

Ember Router Service Polyfill

根据引入该服务的 RFC,有一个 isActive method 可用于检查特定路由当前是否处于活动状态。在不知道 tagline-ticker 的代码的情况下,很难确切地知道它是如何使用的。但是,我会想象您正在迭代传入的 articles,因此您可以执行以下操作:

export default Ember.Component.extends({
  router: Ember.inject.service(),

  articles: undefined,
  filteredArticles: computed('articles', 'router.currentRoute', function() {
    const router = this.get('router');
    return this.get('articles').filter(article => {
       // Return `false` if this particular article is active (YMMV based on your code)
       return !router.isActive('routeForArticle', article); 
    });
  })
});

然后,您可以在您的模板中遍历 filteredArticles,您将只有当前未显示的模板。

我试图扩展 LinkComponent,但我 运行 遇到了几个问题,但仍然无法让它与 current-when 一起工作。此外,如果多个组件需要基于子路由执行相同的逻辑,它们 all 需要从 LinkComponent 扩展并执行相同的样板文件才能使其正常工作。

因此,根据@kumkanillam 的评论,我使用服务实现了这一点。它工作得非常好,除了必须访问组件中某处的服务才能观察它。 (参见 很棒 question/answer。)

services/current-article.js

export default Ember.Service.extend({
  setId(articleId) {
    this.set('id', articleId);
  },
  clearId() {
    this.set('id', null);
  },
});

routes/article.js

export default Ember.Route.extend({
  // Prefer caching currently viewed article ID via service
  // rather than localStorage
  currentArticle: Ember.inject.service('current-article'),

  activate() {
    this._super(arguments);
    this.get('currentArticle').setId(
      this.paramsFor('articles.article').article_id);
  },

  deactivate() {
    this._super(arguments);
    this.get('currentArticle').clearId();
  },

  ... model stuff
});

components/taglines-ticker.js

export default Ember.Component.extend({
  currentArticle: Ember.inject.service('current-article'),

  didReceiveAttrs() {
    // The most annoying thing about this approach is that it 
    // requires accessing the service to be able to observe it
    this.get('currentArticle');
  },

  filteredArticles: computed('currentArticle.id', function() {
    const current = this.get('currentArticle.id');

    return this.get('articles').filter(a => a.get('id') !== current);
  }),
});



更新: 如果改为从 controller/parent 组件传递服务,则可以消除 didReceiveAttrs 挂钩。

controllers/application.js

export default Ember.Controller.extend({
  currentArticle: Ember.inject.service('current-article'),
});

templates/application.hbs

{{taglines-ticker currentArticle=currentArticle}}
  ... model stuff
});

components/taglines-ticker.js

export default Ember.Component.extend({
  filteredArticles: computed('currentArticle.id', function() {
    const current = this.get('currentArticle.id');

    return this.get('articles').filter(a => a.get('id') !== current);
  }),
});