介紹如何使用 template driven 快速建立簡易的互動表單

Angular 2 正式版在日前已經正式發佈了,因此較不會再遇到 alpha 或 beta 甚至 rc 的時候還出現的重大變更(BREAK CHANGE)了,真的出現的話,我想也應該是 Angular 3 要出來的時候了 XD。

先前參考官方文件介紹過 Angular 2 Forms 的用法,這次來自己實作,以 Template-Driven 的方式建立簡單的登入表單,並且介紹完整的運作流程,主要的內容如下:

  • Import FormsModule & 準備 HTML template
  • 在 Template 中的表單控制項加上 ngModel Directive
  • 初始化表單控制項預設值
  • 使用 ngModel 提供的狀態進行 CSS class 裝飾
  • 追蹤表單驗證狀態並顯示錯誤訊息提示

登入畫面,是我們在任何的 web application 中最常看到的頁面,因此,就以登入畫面來當範例練習吧!

首先,import 所需要的 FormsModule

使用 Angular 2 Forms 之前,要先把需要的 Module import 進來,AppModule 的內容如下:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, FormsModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

接著,準備我們的 HTML Template:

<div class="container">
  <h1>Angular 2 Form Demo</h1>
  <form>
    <div class="form-group">
      <label for="email">Email</label>
      <input
        type="email"
        class="form-control"
        id="email"
        name="email"
        placeholder="Email"
      />
    </div>
    <div class="errors">
      <p>請輸入帳號</p>
    </div>
    <div class="form-group">
      <label for="password">Password</label>
      <input
        type="password"
        class="form-control"
        id="password"
        name="password"
        placeholder="Password"
      />
    </div>
    <div class="errors">
      <p>請輸入密碼</p>
    </div>
    <div class="checkbox">
      <label>
        <input id="remember" name="remember" type="checkbox" /> Remember me
      </label>
    </div>
    <button type="submit" class="btn btn-default">Login</button>
  </form>
</div>

這是一個單純的 HTML Template,到目前為止都尚未有任何和 Angular 相關的操作,HTML class 的部分則是套用了 Bootstrap 的樣式,你也可以先在 index.html 上加上 Bootstrap 的樣式來源。讓畫面看起來比較順眼些。準備好以上的前置步驟後,準備開始套用 ngModel 實作 Template-Driven Forms 囉!

在 Template 中的表單控制項加上 ngModel Directive

打開 app.component.html template 檔,在表單中的所有 input 加上 ngModel,如下:

<!--Template-Driven-->
<div class="container">
  <h1>Angular 2 Form Demo</h1>
  <form #myForm="ngForm">
    <div class="form-group">
      <label for="email">Email</label>
      <input
        type="email"
        class="form-control"
        id="email"
        name="email"
        placeholder="Email"
        ngModel
      />
    </div>
    <div class="errors">
      <p>請輸入帳號</p>
    </div>
    <div class="form-group">
      <label for="password">Password</label>
      <input
        type="password"
        class="form-control"
        id="password"
        name="password"
        placeholder="Password"
        ngModel
      />
    </div>
    <div class="errors">
      <p>請輸入密碼</p>
    </div>
    <div class="checkbox">
      <label>
        <input id="remember" name="remember" type="checkbox" ngModel />
        Remember me
      </label>
    </div>
    <button type="button" class="btn btn-default" (click)="doSome(myForm)">
      Login
    </button>
  </form>
</div>

<!-- Show Test Data-->
<hr />
<div class="container">
  <pre>
    {{ myForm.value | json }}
  </pre>
</div>

上面這邊我們主要是在表單中的所有控制項加上 ngModel 屬性,加上後,首先 Angular 就會在這個 template 產生一個 ngForm 的實體,每個有 ngModel directive 的 input 也會產生實體物件,並且存在於 ngForm 實體中,為了可以看到 model 的值,我們在 form 標籤新增一個 template 變數 myForm ,然後在下方新增一個 pre 標籤區塊並加上 Angular 的 expression 來顯示 myForm 的值,執行後的結果如下:

初步建立的 template-driven form

初始化表單預設值

很多時候我們的表單都會需要有預設值的情況,那在 template-driven form 要如何初始化 model 的預設值呢?

我們需要先在 component class 新增一個屬性來存放 model 資料,然後在 template 把原本的 ngModel 改成 [ngModel]="modelName" 就可以囉!

<div class="form-group">
  <label for="email">Email</label>
  <input
    type="email"
    class="form-control"
    id="email"
    name="email"
    placeholder="Email"
    [ngModel]="email"
  />
</div>

使用 ngModel 提供的狀態進行 CSS class 裝飾

還有一種需求佷也很常見,就是依照表單的狀態來顯示不同的 CSS 樣式,一般的做法通常都是自己針對不同狀態來命名 CSS Class,Angular 預設為表單提供了三種類型的狀態 class,我們就可以直接對預設提供的 class 名稱來設計樣式。表單控制項的狀態預設提供的有:

狀態(State ) Class if true Class if false
Control 被點擊接觸過 ng-touched ng-untouched
Control 的值被改變 ng-dirty ng-pristine
Control 的值不符合驗證 ng-valid ng-invalid

追蹤表單驗證狀態並顯示錯誤訊息提示

表單控制項除了依照狀態來顯示 CSS 樣式之外,我們還需要有提示訊息的顯示來讓使用者可以更容易的了解,透過適當的訊息提示可有效幫助 UI 操作,帶來良好的使用者體驗。ngModel 除了依照控制項的狀態來提供 CSS Class 之外,在 formControl 的實體中,也存在著控制項狀態的 model,我們可以依照這些 model 作為訊息顯示的判斷。

一開始準備好的 template 可以看到有 class="errors"div 元素,但一開始進入頁面的時候,其實不需要馬上顯示錯誤,而是要使用者接觸或輸入之後,若有錯誤再出現提示訊息即可。

我們可以在表單控制項加上 template 變數 #userMail 變數名稱可自訂,要以 # 字號為開頭,並指定成 ngModel,然後每個再加上需要的驗證屬性例如:required, minlength, maxlength, pattern 等,這些是 Angular 預設提供的 Validator,套用的方式如下:

<div class="form-group">
  <label for="email">Email</label>
  <input
    type="email"
    class="form-control"
    id="email"
    name="email"
    placeholder="Email"
    [ngModel]="email"
    required
    minlength="5"
    pattern="^[a-zA-Z0-9.!#$%&』*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$"
    #iemail="ngModel"
  />
</div>
<div class="errors" *ngIf="iemail.errors">
  <p *ngIf="iemail.errors.minlength">最少要 5 個字</p>
  <p *ngIf="iemail.errors.required">必填項目</p>
  <p *ngIf="iemail.errors.pattern">請輸入正確的 Email 格式</p>
</div>

Class errors 的 div 的用途用來顯示錯誤訊息的部分,以上面的 input 為例,Angular 建立實體之後,會有一個 errors 的屬性,當有 Validator 驗證錯誤的時候,會存一個物件到 errors 屬性中。這時候我們做了一個 ngIf 來判斷是哪一個錯誤,然後顯示對應的錯誤訊息。另外我們也可以在按鈕設定,當整張表單的驗證狀態為 invalid 的時候,就可以把按鈕 disabled 禁止點擊。

到目前為止的練習,我們的 component 完全沒有任何的邏輯驗證或處理,只設定了一個初始值,所有的驗證都在 template 做完了!Angular 2 Template-Driven Forms 是不是很簡單呢? :)