Ember 中的路由

本文将介绍 **路由**,有时也称为基于 URL 的过滤。我们将使用它为三个待办事项视图(“全部”、“活动”和“已完成”)提供唯一的 URL。

先决条件

至少,建议您熟悉核心 HTMLCSSJavaScript 语言,并了解 终端/命令行

对现代 JavaScript 特性(如类、模块等)的深入了解将非常有益,因为 Ember 大量使用它们。

目标 学习在 Ember 中实现路由。

基于 URL 的筛选

Ember 带有一个与浏览器 URL 紧密集成的路由系统。通常,在编写 Web 应用程序时,您希望页面由 URL 表示,这样如果(出于任何原因)页面需要刷新,用户不会对 Web 应用程序的状态感到意外——他们可以链接到应用程序的重要视图。

目前,我们已经有“全部”页面,因为我们目前没有对我们一直使用的页面进行任何过滤,但我们需要对其进行一些重组,以便处理“活动”和“已完成”待办事项的不同视图。

Ember 应用程序有一个默认的“application”路由,它与 app/templates/application.hbs 模板相关联。因为该应用程序模板是我们待办事项应用程序的入口点,所以我们需要进行一些更改以允许路由。

创建路由

让我们从创建三个新的路由开始:“Index”、“Active”和“Completed”。为此,您需要在应用程序的根目录中将以下命令输入到您的终端中

bash
ember generate route index
ember generate route completed
ember generate route active

第二和第三个命令不仅应该生成了新文件,而且还更新了一个现有文件,app/router.js。它包含以下内容

js
import EmberRouter from "@ember/routing/router";
import config from "./config/environment";

export default class Router extends EmberRouter {
  location = config.locationType;
  rootURL = config.rootURL;
}

Router.map(function () {
  this.route("completed");
  this.route("active");
});

当上面运行第二和第三个命令时,添加了突出显示的行。

router.js 充当开发人员的“站点地图”,以便他们能够快速了解整个应用程序的结构。它还告诉 Ember 如何与您的路由交互,例如在加载任意数据时、在加载数据时处理错误或解释 URL 的动态部分。由于我们的数据是静态的,因此我们不会接触到任何这些花哨的功能,但我们将确保路由提供查看页面所需的最小数据。

创建“Index”路由没有在 router.js 中添加路由定义行,因为与 URL 导航和 JavaScript 模块加载一样,“Index”是一个特殊词,表示要渲染、加载等的默认路由。

为了调整我们渲染 TodoList 应用程序的旧方法,我们首先需要将应用程序模板中的 TodoList 组件调用替换为 {{outlet}} 调用,这意味着“任何子路由将在这里渲染”。

转到 todomvc/app/templates/application.hbs 文件并替换

hbs
<TodoList />

hbs
{{outlet}}

接下来,在我们的 index.hbscompleted.hbsactive.hbs 模板中(也位于模板目录中),我们现在可以只输入 TodoList 组件调用。

在每种情况下,替换

hbs
{{outlet}}

hbs
<TodoList />

因此,如果您现在尝试再次运行应用程序并访问三个路由中的任何一个

localhost:4200 localhost:4200/active localhost:4200/completed

您将看到完全相同的内容。在每个 URL 上,与特定路径(“Active”、“Completed”或“Index”)对应的模板将渲染 <TodoList /> 组件。<TodoList /> 在页面中渲染的位置由父路由中的 {{ outlet }} 确定,在本例中为 application.hbs。所以我们已经有了路由。太好了!

但现在我们需要一种方法来区分这些路由,以便它们显示它们应该显示的内容。

首先,再次回到我们的 todo-data.js 文件。它已经包含一个返回所有待办事项的 getter,以及一个返回未完成待办事项的 getter。我们缺少的 getter 是一个仅返回已完成待办事项的 getter。将以下内容添加到现有 getter 下面

js
get completed() {
  return this.todos.filter((todo) => todo.isCompleted);
}

模型

现在我们需要将模型添加到我们的路由 JavaScript 文件中,以便我们可以轻松地返回特定数据集以在这些模型中显示。model 是一个数据加载生命周期钩子。对于 TodoMVC,模型的功能对我们来说并不那么重要;如果您想深入了解,可以在 Ember 模型指南 中找到更多信息。我们还提供了对服务的访问,就像我们对组件所做的那样。

索引路由模型

首先,更新 todomvc/app/routes/index.js 使其如下所示

js
import Route from "@ember/routing/route";
import { inject as service } from "@ember/service";

export default class IndexRoute extends Route {
  @service("todo-data") todos;

  model() {
    let todos = this.todos;

    return {
      get allTodos() {
        return todos.all;
      },
    };
  }
}

现在我们可以更新 todomvc/app/templates/index.hbs 文件,以便它包含 <TodoList /> 组件时,会显式地使用可用的模型,调用其 allTodos() getter 以确保显示所有待办事项。

在此文件中,更改

hbs
<TodoList />

hbs
<TodoList @todos={{ @model.allTodos }} />

已完成路由模型

现在更新 todomvc/app/routes/completed.js 使其如下所示

js
import Route from "@ember/routing/route";
import { inject as service } from "@ember/service";

export default class CompletedRoute extends Route {
  @service("todo-data") todos;

  model() {
    let todos = this.todos;

    return {
      get completedTodos() {
        return todos.completed;
      },
    };
  }
}

现在我们可以更新 todomvc/app/templates/completed.hbs 文件,以便它包含 <TodoList /> 组件时,会显式地使用可用的模型,调用其 completedTodos() getter 以确保仅显示已完成的待办事项。

在此文件中,更改

hbs
<TodoList />

hbs
<TodoList @todos={{ @model.completedTodos }} />

活动路由模型

最后,对于路由,让我们整理一下我们的活动路由。从更新 todomvc/app/routes/active.js 开始,使其如下所示

js
import Route from "@ember/routing/route";
import { inject as service } from "@ember/service";

export default class ActiveRoute extends Route {
  @service("todo-data") todos;

  model() {
    let todos = this.todos;

    return {
      get activeTodos() {
        return todos.incomplete;
      },
    };
  }
}

现在我们可以更新 todomvc/app/templates/active.hbs 文件,以便它包含 <TodoList /> 组件时,会显式地使用可用的模型,调用其 activeTodos() getter 以确保仅显示活动(未完成)的待办事项。

在此文件中,更改

hbs
<TodoList />

hbs
<TodoList @todos={{ @model.activeTodos }} />

请注意,在每个路由模型钩子中,我们都返回一个带有 getter 的对象,而不是一个静态对象,或者更多地是静态的待办事项列表(例如,this.todos.completed)。这样做是为了让模板对待办事项列表有一个动态引用,如果我们直接返回列表,数据将永远不会重新计算,这将导致导航看起来像失败/没有实际过滤。通过在模型数据返回对象中定义一个 getter,待办事项将被重新查找,以便我们对待办事项列表的更改会在渲染的列表中体现出来。

因此,我们的路由功能现在都已到位,但我们无法从应用程序中访问它们。让我们使页脚链接处于活动状态,以便单击它们会转到所需的路由。

回到 todomvc/app/components/footer.hbs,找到以下标记片段

hbs
<a href="#">All</a>
<a href="#">Active</a>
<a href="#">Completed</a>

更新为

hbs
<LinkTo @route="index">All</LinkTo>
<LinkTo @route="active">Active</LinkTo>
<LinkTo @route="completed">Completed</LinkTo>

<LinkTo> 是一个内置的 Ember 组件,它处理导航路由时所有状态更改,以及在任何与 URL 匹配的链接上设置“active”类,以防需要将其与非活动链接区分开来。

更新 TodoList 中的待办事项显示

我们需要修复的最后一件小事是,以前,在 todomvc/app/components/todo-list.hbs 中,我们直接访问了 todo-data 服务并循环遍历所有待办事项,如下所示

hbs
{{#each this.todos.all as |todo| }}

由于我们现在希望 TodoList 组件显示一个过滤后的列表,因此我们希望向 TodoList 组件传递一个参数,该参数代表“当前待办事项列表”,如下所示

hbs
{{#each @todos as |todo| }}

本教程到此结束!您的应用程序现在应该在页脚中有完全有效的链接,显示“Index”/默认、“Active”和“Completed”路由。

The todo list app, showing the routing working for all, active, and completed todos.

总结

恭喜!您已完成本教程!

在我们将此处介绍的内容与原始的 TodoMVC 应用程序 相比,还有很多东西要实现,例如编辑、删除和跨页面重新加载持久化待办事项。

要查看我们完成的 Ember 实现,请查看本教程代码的存储库中完成的应用程序文件夹 此处,或查看 实时部署版本。研究代码以了解有关 Ember 的更多信息,并查看下一篇文章,其中提供指向更多资源的链接以及一些故障排除建议。