Ember 中的路由
在本文中,我们将学习路由,有时也称作基于 URL 的过滤。我们将使用它为三个待办事项视图(“所有”、“活动”和“已完成”)提供一个唯一的 URL。
| 预备知识 |
至少,建议您熟悉核心 HTML、CSS 和 JavaScript 语言,并了解 终端/命令行。 Ember 大量使用现代 JavaScript 特性(如类、模块等),因此深入理解这些特性将非常有益。 |
|---|---|
| 目标 | 学习在 Ember 中实现路由。 |
基于 URL 的过滤
Ember 提供了一个与浏览器 URL 紧密集成的路由系统。通常,在编写 Web 应用程序时,您希望页面由 URL 表示,这样如果(出于任何原因)页面需要刷新,用户不会对 Web 应用程序的状态感到惊讶 — 他们可以直接链接到应用程序的重要视图。
目前,我们已经有了“所有”页面,因为我们当前正在处理的页面没有进行任何过滤,但我们需要对其进行一些重组,以处理“活动”和“已完成”待办事项的不同视图。
Ember 应用程序有一个默认的“application”路由,它与 app/templates/application.hbs 模板关联。由于该应用程序模板是我们待办事项应用程序的入口点,我们需要进行一些更改以允许路由。
创建路由
让我们从创建三个新路由开始:“Index”、“Active”和“Completed”。为此,您需要在应用程序的根目录中,在终端中输入以下命令
ember generate route index
ember generate route completed
ember generate route active
第二个和第三个命令不仅应该生成了新文件,还更新了一个现有文件 app/router.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 文件,并将
<TodoList />
替换为
{{outlet}}
接下来,在我们的 index.hbs、completed.hbs 和 active.hbs 模板(也在模板目录中)中,我们现在可以只输入 TodoList 组件调用。
在每种情况下,替换
{{outlet}}
with
<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 下方添加以下内容
export default class TodoDataService extends Service {
// …
get completed() {
return this.todos.filter((todo) => todo.isCompleted);
}
// …
}
模型
现在我们需要将模型添加到我们的路由 JavaScript 文件中,以便我们可以轻松地返回特定的数据集以在这些模型中显示。model 是一个数据加载生命周期钩子。对于 TodoMVC,模型的功能对我们来说并不那么重要;如果您想深入了解,可以在 Ember 模型指南 中找到更多信息。我们还提供了对服务的访问,就像我们对组件所做的那样。
索引路由模型
首先,更新 todomvc/app/routes/index.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,以确保显示所有待办事项。
在此文件中,更改
<TodoList />
至
<TodoList @todos={{ @model.allTodos }} />
已完成路由模型
现在更新 todomvc/app/routes/completed.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,以确保只显示已完成的待办事项。
在此文件中,更改
<TodoList />
至
<TodoList @todos={{ @model.completedTodos }} />
活动路由模型
最后对于路由,让我们整理我们的活动路由。首先更新 todomvc/app/routes/active.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,以确保只显示活动的(未完成的)待办事项。
在此文件中,更改
<TodoList />
至
<TodoList @todos={{ @model.activeTodos }} />
请注意,在每个路由模型钩子中,我们都返回一个带有 getter 的对象,而不是一个静态对象,或者仅仅是待办事项的静态列表(例如 this.todos.completed)。这样做的原因是,我们希望模板对待办事项列表有一个动态引用,如果我们直接返回列表,数据将永远不会重新计算,这将导致导航看起来失败/实际上没有过滤。通过在模型数据的返回对象中定义一个 getter,待办事项会重新查找,以便我们对 todo 列表的更改反映在渲染列表中。
让页脚链接正常工作
所以我们的路由功能现在都已就位,但我们无法从我们的应用程序中访问它们。让我们激活页脚链接,以便点击它们可以转到所需的路由。
回到 todomvc/app/components/footer.hbs,找到以下标记
<a href="#">All</a>
<a href="#">Active</a>
<a href="#">Completed</a>
将其更新为
<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 服务并循环遍历所有待办事项,如下所示
{{#each this.todos.all as |todo| }}
由于我们现在希望 TodoList 组件显示一个过滤列表,我们将需要向 TodoList 组件传递一个表示“当前待办事项列表”的参数,如下所示
{{#each @todos as |todo| }}
本次教程到此结束!您的应用程序现在应该在页脚中拥有完全可用的链接,可以显示“Index”/默认、“Active”和“Completed”路由。

总结
恭喜!您已完成本教程!
在与原始 TodoMVC 应用程序 功能保持一致之前,我们还需要实现更多功能,例如编辑、删除和在页面重新加载时保留待办事项。
要查看我们完成的 Ember 实现,请查看 本教程代码 仓库中的已完成应用程序文件夹,或在此处查看 实时部署版本。学习代码以了解更多关于 Ember 的信息,并查看下一篇文章,其中提供了更多资源链接和一些故障排除建议。