iT邦幫忙

2021 iThome 鐵人賽

DAY 12
0
Modern Web

關於我作夢變成工程師這檔事(Angular 篇)系列 第 12

第 12 天 範本變數加範本輸入變數|template variables、template input variables

前情提要

簡單地聊聊範本驅動表單(Template Driven Fomrs)後,應該會覺得範本變數(Template Variables)常常出現吧。範本變數讓我們很容易地在 HTML 上,透過宣告一個變數,來取得特定對象、使用特定對象的相關內容(屬性、方法...)。

今天,讓我們更深入地討論範本變數,並且搭配範本輸入變數(Template Input Variable)來玩玩:)

範本變數的常用情景

依據官方文件,範本變數可以參考的對象,幾乎可以說是 html 檔案中可以看到的的所有對象。但我們最常使用到的,應該是下列三種場景:

  • 取得自訂元件的 DOM:例如,取得自訂的 Stepper 元件,這樣就可以很容易地在 HTML 的按鈕來使用 Stepper 元件的提供的上一步、下一步方法。
<app-stepper #tStepper></app-stepper>
<button type="button" (click)="tStepper.previous()">上一步</button>
<button type="button" (click)="tStepper.next()">下一步</button>
  • 取得 HTML 元素:例如,取得 <Input> 的 value。
<input #tPhone />
<button tpe="button" (click)="callPhone(tPhone.value)">撥打電話</button>
  • 取得 TemplateRef:取得 <ng-template></ng-template>,搭配結構性指令 *ngIf 就像是在 html 檔案中使用預先準備好的模板一般,例如:
<ng-container 
    *ngIf="heroList.length 
         then tHeroList"
         else tNoData>
</ng-container>

<ng-template #tHeroList>
    <app-hero-item 
        *ngFor="let hero of heroList"
        [hero]="hero">
    </app-hero-item>
</ng-template>

<ng-template #tNoData>
    沒有英雄資料
</ng-template>
  • 搭配 FomsModule,取得 NgForm 或 NgModel 創建的表單控制項實例:這就是範本變數與範本驅動表單最常的搭配方式,當 <Input> 上掛載 ngModel 指令時,就會產生一個 exportAs 的 ngModel 實例,此時,就可以將這個實例賦予給同標籤上的範本變數:
<label for="email">Email</label>
<input
    name="email"
    id="email"
    #tEmail="ngModel"
    type="email"
    ngModel
    email
    required/>

這樣就可以使用表單控制項(FormControl):

<!-- 當 tEmail 不合法的時候,就顯示這個 paragraph -->
<!-- 依據 errors 顯示驗證提示訊息 -->
<ol *ngIf="tName.invalid">
    <li *ngIf="tName.errors?.required">此欄位必填</li>
    <li *ngIf="tName.errors?.email">請輸入合法的 Email 格式</li>
</ol>

特別要提一下的是,在模組檔案中匯入 FormsModule 後。所有 <form> 標籤自動加上 ngForm 指令,這也是為什麼 <input> 需要先使用 ngModel 指令,才能將表單控制項的實例賦予給範本變數,而 <form> 就不必加上 ngForm 指令就可以將它的控制項賦予給範本變數:

<form #tForm="ngForm" (ngSubmit)="doSubmit(tForm.value)">
    <input #tName="ngModel" ngModel/>
</form>

看了這項使用案例,大概就會有想像,可以組合這些東西完成好玩的事情吧!不僅如此,還有個東西叫做範本輸入變數(Template Input Variables),現在我們就在範本驅動表單上,使用範本變數加上範本輸入變數來玩玩檢核訊息吧!

範本變數搭配範本輸入變數

在 HeroInformationForm 中,幾乎所有的欄位都是必填的。當使用者沒有填寫時,我們就要顯示「此欄位必填!」的訊息,同樣的錯誤訊息寫那麼多次實在有點煩躁...剛剛是不是說,有個東西可以像 html 檔案上可以呼叫的方法呢?讓我們這樣調整:

<!-- Template methods -->
<ng-template #tRequiredError>
  <div>
    This field is required!
  </div>
</ng-template>

之後在每個需要顯示此資訊的地方,使用 *ngIf 來呼叫這個方法:

 <ng-container
      *ngIf="tName.invalid && tForm.submitted"
    >
        <ng-container
          *ngIf="tName.errors?.required then tRequiredError">
        </ng-container>
    </ng-container>

如果 tName.errors.required(檢核錯誤:必填)為真時,就會呼叫 tRequiredError 的 TemplateRef ,用它來替換 <ng-container> 容器。這樣,日後如果要填整檢核資訊,我們就只需要調整 #tRequiredError 所指的 <ng-template> 內容就可以了。

接下來,我們要搭配範本輸入變數,來進一步優惠這個必填檢核訊息:

<ng-template #tRequiredError let-fieldName="fieldName">
  <div>
    {{ fieldName }} is required!
  </div>
</ng-template>

我們在這個 TemplateRef 上加上了輸入變數 fieldName,並透過 let 的掛載,讓我們在使 <ng-template> 的範圍中,可以使用這個 fieldName 變數。

而使用方法是:

  <div
    *ngIf="tName.errors?.required"
    class="error-message"
    >
    <ng-container
      *ngTemplateOutlet="tRequiredError;
      context: { fieldName: 'Name' }">
    </ng-container>
  </div>

只要使用 *ngTemplateOutlet 指令來放置 tReqiredError 此 TemplateRef,就可以一併輸入 context 物件,在這個物件裡面將 fieldName 屬性賦予對應的欄位名稱,這樣會出現的檢核訊息就是:

https://ithelp.ithome.com.tw/upload/images/20210927/201283959jtfwYcNTI.png

很好玩吧哈哈!


上一篇
第 11 天 範本驅動表單的動態檢核訊息|ngSubmit
下一篇
第 13 天 長痛不如短痛!整理專案|feature module、shared module
系列文
關於我作夢變成工程師這檔事(Angular 篇)14

尚未有邦友留言

立即登入留言