Ember 中的路由

在本文中,我們將學習路由,有時也稱作基於 URL 的過濾。我們將使用它為三個待辦事項檢視(“所有”、“活動”和“已完成”)提供一個唯一的 URL。

預備知識

至少,建議您熟悉核心 HTMLCSSJavaScript 語言,並瞭解 終端/命令列

Ember 大量使用現代 JavaScript 特性(如類、模組等),因此深入理解這些特性將非常有益。

目標 學習在 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}}

with

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 下方新增以下內容

js
export default class TodoDataService extends Service {
  // …
  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,待辦事項會重新查詢,以便我們對 todo 列表的更改反映在渲染列表中。

所以我們的路由功能現在都已就位,但我們無法從我們的應用程式中訪問它們。讓我們啟用頁尾連結,以便點選它們可以轉到所需的路由。

回到 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 的資訊,並檢視下一篇文章,其中提供了更多資源連結和一些故障排除建議。