Ember 交互性:页脚功能、条件渲染

现在是时候开始处理我们应用程序中的页脚功能了。在这里,我们将使待办事项计数器更新以显示待完成的待办事项的正确数量,并正确地将样式应用于已完成的待办事项(即已选中复选框的待办事项)。我们还将连接我们的“清除已完成”按钮。在此过程中,我们将学习如何在模板中使用条件渲染。

先决条件

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

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

目标 继续学习关于组件类的知识,开始了解条件渲染,并连接一些页脚功能。

要使页脚正常工作,我们需要实现以下三个功能区域

  • 待办事项计数器。
  • 所有、活动和已完成待办事项的筛选器。
  • 一个清除已完成待办事项的按钮。
  1. 因为我们需要从页脚组件访问我们的服务,所以我们需要为页脚生成一个类。输入以下终端命令来执行此操作
    bash
    ember generate component-class footer
    
  2. 接下来,找到新创建的 todomvc/app/components/footer.js 文件,并将其更新为以下内容
    js
    import Component from "@glimmer/component";
    import { inject as service } from "@ember/service";
    
    export default class FooterComponent extends Component {
      @service("todo-data") todos;
    }
    
  3. 现在我们需要回到我们的 todo-data.js 文件中,并添加一些功能,使我们能够返回未完成的待办事项的数量(对于显示剩余数量很有用),并从列表中清除已完成的待办事项(这就是“清除已完成”功能所需的功能)。在 todo-data.js 中,在现有的 all() getter 下方添加以下 getter,以定义未完成的待办事项实际上是什么
    js
    get incomplete() {
      return this.todos.filterBy('isCompleted', false);
    }
    
    使用 Ember 的 ArrayProxy.filterBy() 方法,我们可以根据简单的相等条件轻松地过滤数组中的对象。在这里,我们要求所有 isCompleted 属性等于 false 的待办事项,并且因为 isCompleted 在我们的 Todo 对象中是 @tracked 的,所以这个 getter 会在数组中对象的值发生变化时重新计算。
  4. 接下来,在现有的 add(text) 动作下方添加以下动作
    js
    @action
    clearCompleted() {
      this.todos = this.incomplete;
    }
    
    这对于清除待办事项非常不错——我们只需要将 todos 数组设置为等于未完成待办事项的列表。
  5. 最后,我们需要在我们的 footer.hbs 模板中使用这个新功能。现在转到这个文件。
  6. 首先,替换这行
    hbs
    <strong>0</strong> todos left
    
    用这个,它使用 incomplete 数组的长度填充未完成的数量
    hbs
    <strong>{{this.todos.incomplete.length}}</strong> todos left
    
  7. 接下来,替换这个
    hbs
    <button type="button" class="clear-completed">
    
    用这个
    hbs
    <button type="button" class="clear-completed" {{on 'click' this.todos.clearCompleted}}>
    

因此,现在当单击按钮时,我们之前添加的 clearCompleted() 动作将运行。但是,如果您尝试单击“清除已完成”按钮,它似乎不会做任何事情,因为目前还没有办法“完成”待办事项。我们需要将 todo.hbs 模板连接到服务,以便选中相关复选框会更改每个待办事项的状态。我们将在下一步中执行此操作。

待办事项/待办事项复数问题

以上内容很好,但我们还有另一个需要解决的小问题。即使只有一个待办事项剩下,"剩余待办事项"指示器也总是显示“x 个待办事项剩下”,这语法错误!

要解决此问题,我们需要更新模板的这部分以包含一些条件渲染。在 Ember 中,您可以使用 条件内容 条件地渲染模板的某些部分;一个简单的块示例如下所示

hbs
{{#if this.thingIsTrue}} Content for the block form of "if"
{{/if}}

所以让我们尝试替换 footer.hbs 的这部分

hbs
<strong>{{this.todos.incomplete.length}}</strong> todos left

用以下内容

hbs
<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)。

js
get todoCountIsOne() {
  return this.incomplete.length === 1;
}

然后回到 footer.hbs,并将我们之前编辑的模板部分更新为以下内容

hbs
<strong>{{this.todos.incomplete.length}}</strong>
{{#if this.todos.todoCountIsOne}}todo{{else}}todos{{/if}} left

现在保存并测试,您将看到当您只有一个待办事项时会使用正确的复数形式!

请注意,这是 Ember 中 if 的块形式;您也可以使用内联形式

hbs
{{if this.todos.todoCountIsOne "todo" "todos"}}

完成待办事项

与其他组件一样,我们需要一个类来访问服务。

创建待办事项类

  1. 在您的终端中运行以下命令
    bash
    ember generate component-class todo
    
  2. 现在转到新创建的 todomvc/app/components/todo.js 文件,并将内容更新为如下所示,以便待办事项组件可以访问服务
    js
    import Component from "@glimmer/component";
    import { inject as service } from "@ember/service";
    
    export default class TodoComponent extends Component {
      @service("todo-data") todos;
    }
    
  3. 接下来,再次回到我们的 todo-data.js 服务文件,并在之前动作下方添加以下动作,这将使我们能够切换每个待办事项的完成状态
    js
    @action
    toggleCompletion(todo) {
      todo.isCompleted = !todo.isCompleted;
    }
    

更新模板以显示已完成状态

最后,我们将编辑 todo.hbs 模板,以便复选框的值现在绑定到待办事项上的 isCompleted 属性,并且当发生更改时,会调用待办事项服务上的 toggleCompletion() 方法。

  1. todo.hbs 中,首先找到以下行
    hbs
    <li>
    
    并用这个替换它——您会注意到,这里我们使用了一些条件内容来添加适当的类值
    hbs
    <li class={{ if @todo.isCompleted 'completed' }}>
    
  2. 接下来,找到以下行
    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) }}
    >
    

    注意:以上代码片段使用了一个新的 Ember 特定的关键字——fnfn 允许 部分应用,这类似于 bind,但它永远不会改变调用上下文;这等效于使用第一个参数为 nullbind

尝试重新启动开发服务器并再次转到 localhost:4200,您现在将看到我们有一个完全可操作的“剩余待办事项”计数器和清除按钮

todos being marked as complete, and cleared

如果您想知道为什么我们没有直接在组件上进行切换(因为该函数完全自包含,并且不需要来自服务中的任何内容),那么您问这个问题完全正确!但是,因为我们最终会希望将所有待办事项列表的更改持久化或同步到 本地存储(参见 应用程序的最终版本),将所有更改持久化状态的操作放在同一个地方是有意义的。

总结

现在就这些了。至此,我们不仅可以将待办事项标记为已完成,还可以清除它们。现在唯一剩下要连接到页脚的是三个筛选链接:“全部”、“活动”和“已完成”。我们将在下一篇文章中使用路由来实现此操作。