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

如果您想知道為什麼我們沒有直接在元件上進行切換(因為該函式完全自包含,並且不需要來自服務中的任何內容),那麼您問這個問題完全正確!但是,因為我們最終會希望將所有待辦事項列表的更改持久化或同步到 本地儲存(參見 應用程式的最終版本),將所有更改持久化狀態的操作放在同一個地方是有意義的。

總結

現在就這些了。至此,我們不僅可以將待辦事項標記為已完成,還可以清除它們。現在唯一剩下要連線到頁尾的是三個篩選連結:“全部”、“活動”和“已完成”。我們將在下一篇文章中使用路由來實現此操作。