在 Ember.js 中组织列表、查看、创建和更新路线的最佳实践

Best practice to organize list, view, create and update routes in Ember.js

现在在 ember.js 工作了几年,我仍然不太清楚,什么应该被视为构建列表、查看、创建和更新路由的最佳实践。

到目前为止,我参与的项目主要用于按实体路由树。用于列表的复数实体名称,带有用于创建的子路径,用于详细视图的单数实体名称,带有用于编辑的子路径。例如,post 模型将具有这些路由:/posts 用于列出 posts,/posts/new 用于创建功能,/post/:post_id 用于显示单个 post 和 /post/:post_id/edit 用于编辑那个。相应的路由器如下所示:

Router.map(function() {
  this.route('post', { path: '/post/:post_id' }, function() {
    this.route('edit');
  });
  this.route('posts', function() {
    this.route('new');
  });
});

这种方法对于细节视图和编辑视图非常有效,因为它们共享相同的模型。所以编辑路径的模型钩子可以重用详细视图路径的模型。在 ember 代码中,如下所示:

// app/routes/post.js
import Route from '@ember/routing/route';

export default Route.extend({
  model({ post_id }) {
    return this.get('store').findRecord('post', post_id);
  }
});

// app/routes/post/edit.js
import Route from '@ember/routing/route';

export default Route.extend({
  model() {
    return this.modelFor('post');
  }
});

通常我们会 return 来自 posts 路由模型挂钩的 post 的集合,而不是实现 posts.new 路由的模型挂钩(或 return 根据体系结构在那里设置 POJO / Changeset 但这不是这里的问题)。假设我们没有实现 posts.new 的模型钩子,路由看起来像:

// app/routes/posts.js
import Route from '@ember/routing/route';

export default Route.extend({
  model({ post_id }) {
    return this.get('store').findAll('post');
  }
});

// app/routes/posts/new.js
import Route from '@ember/routing/route';

export default Route.extend({
});

但现在这种方法不再有效,导致到 posts.new 路由的转换被阻止,直到 posts 的集合被加载。因为我们不需要这个集合来创建 post 的列表(至少如果我们只在 posts.index 路由而不是所有子路由中显示它们)这感觉不对。

对那些不太熟悉的人的旁注 ember:嵌套路由模型挂钩按顺序执行。所以在我们的例子中,首先是应用程序路由的模型钩子,然后是 posts 路由,然后是 posts.new 路由等待其中一个执行的任何承诺。

那么什么才是最佳实践?

如果对此有任何想法,我们将不胜感激。决定不在社区闲聊中提出该问题,以便更好地记录答案。

在 ember 中使用 nested route 的要点是将子路由的输出嵌套在父路由中。虽然您当前的结构有效,但它与 ember 具有结构化路由功能的方式并不匹配。

您应该使用具有明确定义的单一嵌套路由 index route

At every level of nesting (including the top level), Ember automatically provides a route for the / path named index. To see when a new level of nesting occurs, check the router, whenever you see a function, that's a new level.

Router.map(function() {
  this.route('posts', function() {
    this.route('favorites');
  });
});

等同于

Router.map(function() {
  this.route('index', { path: '/' });
  this.route('posts', function() {
    this.route('index', { path: '/' });
    this.route('favorites');
  });
});

如果您创建一个明确的 posts/index.js 文件,这可以用作您的列表路由。这样做将帮助您避免在转换到创建路由之前获取所有 post 的问题。

虽然与您目前的结构不同,但我建议如下。

Router.map(function() {
  this.route('posts', function() {
      this.route('index');  // /posts  - posts/index.js
      this.route('new');    // /posts/new - posts/new.js
      this.route('view', { path: '/:post_id' }  // /posts/1234 - posts/view.js
      this.route('edit', { path: '/:post_id/edit' }  // /posts/1234/edit - posts/edit.js
  }); 
});

根据新建和编辑逻辑的复杂程度,可以考虑将两条路由合二为一,或者在生成空模型后直接将新建转为编辑。

这样做的好处包括:

简单 您不必为所有路线重新定义路径。一切都在 posts/ 下,路线指定下一段。

一致性 JSONapi 模式使用 plural routes 来获取集合和单个对象。

逻辑换行 如果您使用显式 index.js 文件,则可以使用旧的 posts.js 文件作为 post 命名空间中所有项目的通用包装器。 Posts.js 将有一个出口,索引、新建、编辑和查看路线被放入

如果您需要您的视图和编辑路线共享同一代模型,您可以将您的 view/edit 嵌套到一个公共组中,以便它们共享一个父模型。

this.route('posts', function() {
    this.route('post', { path: '/:post_id' }, function() {
        this.route('view', { path: '/' }) ;
        this.route('edit', { path: '/edit' });
    })
})