Ember 交互性:页脚功能、条件渲染
现在是时候开始处理我们应用程序中的页脚功能了。在这里,我们将使待办事项计数器更新以显示待完成的待办事项的正确数量,并正确地将样式应用于已完成的待办事项(即已选中复选框的待办事项)。我们还将连接我们的“清除已完成”按钮。在此过程中,我们将学习如何在模板中使用条件渲染。
先决条件 |
至少,建议您熟悉核心 HTML、CSS 和 JavaScript 语言,并了解 终端/命令行。 更深入地了解现代 JavaScript 特性(如类、模块等)将非常有益,因为 Ember 大量使用它们。 |
---|---|
目标 | 继续学习关于组件类的知识,开始了解条件渲染,并连接一些页脚功能。 |
连接页脚的行为
要使页脚正常工作,我们需要实现以下三个功能区域
- 待办事项计数器。
- 所有、活动和已完成待办事项的筛选器。
- 一个清除已完成待办事项的按钮。
- 因为我们需要从页脚组件访问我们的服务,所以我们需要为页脚生成一个类。输入以下终端命令来执行此操作bash
ember generate component-class footer
- 接下来,找到新创建的
todomvc/app/components/footer.js
文件,并将其更新为以下内容jsimport Component from "@glimmer/component"; import { inject as service } from "@ember/service"; export default class FooterComponent extends Component { @service("todo-data") todos; }
- 现在我们需要回到我们的
todo-data.js
文件中,并添加一些功能,使我们能够返回未完成的待办事项的数量(对于显示剩余数量很有用),并从列表中清除已完成的待办事项(这就是“清除已完成”功能所需的功能)。在todo-data.js
中,在现有的all()
getter 下方添加以下 getter,以定义未完成的待办事项实际上是什么使用 Ember 的jsget incomplete() { return this.todos.filterBy('isCompleted', false); }
ArrayProxy.filterBy()
方法,我们可以根据简单的相等条件轻松地过滤数组中的对象。在这里,我们要求所有isCompleted
属性等于false
的待办事项,并且因为isCompleted
在我们的Todo
对象中是@tracked
的,所以这个 getter 会在数组中对象的值发生变化时重新计算。 - 接下来,在现有的
add(text)
动作下方添加以下动作这对于清除待办事项非常不错——我们只需要将js@action clearCompleted() { this.todos = this.incomplete; }
todos
数组设置为等于未完成待办事项的列表。 - 最后,我们需要在我们的
footer.hbs
模板中使用这个新功能。现在转到这个文件。 - 首先,替换这行用这个,它使用hbs
<strong>0</strong> todos left
incomplete
数组的长度填充未完成的数量hbs<strong>{{this.todos.incomplete.length}}</strong> todos left
- 接下来,替换这个用这个hbs
<button type="button" class="clear-completed">
hbs<button type="button" class="clear-completed" {{on 'click' this.todos.clearCompleted}}>
因此,现在当单击按钮时,我们之前添加的 clearCompleted()
动作将运行。但是,如果您尝试单击“清除已完成”按钮,它似乎不会做任何事情,因为目前还没有办法“完成”待办事项。我们需要将 todo.hbs
模板连接到服务,以便选中相关复选框会更改每个待办事项的状态。我们将在下一步中执行此操作。
待办事项/待办事项复数问题
以上内容很好,但我们还有另一个需要解决的小问题。即使只有一个待办事项剩下,"剩余待办事项"指示器也总是显示“x 个待办事项剩下”,这语法错误!
要解决此问题,我们需要更新模板的这部分以包含一些条件渲染。在 Ember 中,您可以使用 条件内容 条件地渲染模板的某些部分;一个简单的块示例如下所示
{{#if this.thingIsTrue}} Content for the block form of "if"
{{/if}}
所以让我们尝试替换 footer.hbs
的这部分
<strong>{{this.todos.incomplete.length}}</strong> todos left
用以下内容
<strong>{{this.todos.incomplete.length}}</strong>
{{#if this.todos.incomplete.length === 1}} todo
{{else}} todos
{{/if}} left
但是这会给我们一个错误——在 Ember 中,这些简单的 if 语句目前只能测试真值/假值,而不是更复杂的表达式,例如比较。要解决此问题,我们必须在 todo-data.js
中添加一个 getter 以返回 this.incomplete.length === 1
的结果,然后在我们的模板中调用它。
在现有的 getter 下方将以下新 getter 添加到 todo-data.js
。请注意,这里我们需要 this.incomplete.length
,而不是 this.todos.incomplete.length
,因为我们在服务内部进行此操作,在那里 incomplete()
getter 可直接使用(在模板中,服务的内容通过 footer
类中的 @service('todo-data') todos;
行在 todos
中可用,因此在那里它是 this.todos.incomplete.length
)。
get todoCountIsOne() {
return this.incomplete.length === 1;
}
然后回到 footer.hbs
,并将我们之前编辑的模板部分更新为以下内容
<strong>{{this.todos.incomplete.length}}</strong>
{{#if this.todos.todoCountIsOne}}todo{{else}}todos{{/if}} left
现在保存并测试,您将看到当您只有一个待办事项时会使用正确的复数形式!
请注意,这是 Ember 中 if
的块形式;您也可以使用内联形式
{{if this.todos.todoCountIsOne "todo" "todos"}}
完成待办事项
与其他组件一样,我们需要一个类来访问服务。
创建待办事项类
- 在您的终端中运行以下命令bash
ember generate component-class todo
- 现在转到新创建的
todomvc/app/components/todo.js
文件,并将内容更新为如下所示,以便待办事项组件可以访问服务jsimport Component from "@glimmer/component"; import { inject as service } from "@ember/service"; export default class TodoComponent extends Component { @service("todo-data") todos; }
- 接下来,再次回到我们的
todo-data.js
服务文件,并在之前动作下方添加以下动作,这将使我们能够切换每个待办事项的完成状态js@action toggleCompletion(todo) { todo.isCompleted = !todo.isCompleted; }
更新模板以显示已完成状态
最后,我们将编辑 todo.hbs
模板,以便复选框的值现在绑定到待办事项上的 isCompleted
属性,并且当发生更改时,会调用待办事项服务上的 toggleCompletion()
方法。
- 在
todo.hbs
中,首先找到以下行并用这个替换它——您会注意到,这里我们使用了一些条件内容来添加适当的类值hbs<li>
hbs<li class={{ if @todo.isCompleted 'completed' }}>
- 接下来,找到以下行并用这个替换它hbs
<input aria-label="Toggle the completion of this todo" class="toggle" type="checkbox" >
hbs<input class="toggle" type="checkbox" aria-label="Toggle the completion of this todo" checked={{ @todo.isCompleted }} {{ on 'change' (fn this.todos.toggleCompletion @todo) }} >
尝试重新启动开发服务器并再次转到 localhost:4200
,您现在将看到我们有一个完全可操作的“剩余待办事项”计数器和清除按钮
如果您想知道为什么我们没有直接在组件上进行切换(因为该函数完全自包含,并且不需要来自服务中的任何内容),那么您问这个问题完全正确!但是,因为我们最终会希望将所有待办事项列表的更改持久化或同步到 本地存储(参见 应用程序的最终版本),将所有更改持久化状态的操作放在同一个地方是有意义的。