三:Angular 模板 (Templates)


1 什么是模板

  它是頁面呈現的骨架,是盛裝組件數據的容器。與之相關的內容包括了-----模板與組件件 '數據交互'、'內置指令'、'表單'、'管道'等。

1.1 模板語法概覽

//插值:綁定屬性變量的值到模板中
//插值:綁定屬性變 <p>{{ detail.telNum }}</p>

//DOM元素屬性綁定:將模板表達式name的值綁定到元素<div>的屬性title上
<div [title]="name"></div>

//HTML標簽特性綁定:將模板表達式的返回值綁定到元素<td>標簽特性colspan上
<td [attr.colspan]="{{ 1+2 }}">合並單元格</td>

//Class類綁定:當isBlue()函數值為true時為div添加類名為isBlue的樣式
<div [class.isblue]="isBlue()"></div>

//Style樣式綁定:當表達式isRed的值為為true時設置button的文字顏色為紅色,否則為綠色
<button [style.color]="isRed ? 'red':'green'">紅色</button>

//事件綁定:單擊元素時會觸發click事件,需要時也可以傳遞$event對象,如(click)="editContact($event)"
<a class='edit' (click)="editContact()"></a>

//雙向綁定:組件和模板間雙向數據綁定,等價於<div [title]="name" (titleChange)="name=$event"></div>
<div [(title)]="name"></div>

//模板局部變量:在當前模板中創建一個對id值為name的input元素的引用變量name,相當於document.getElementById("name")
<input type='text' ##name name="name" id="name"/>

//管道操作符:原始數據birthday經管道轉換后輸出期望數據並顯示在模板中
<p>張三的生日是{{ birthday | date }}</p>

//模板表達式操作符:模板表達式操作符表明detail.telNum屬性不是必須存在的,如果它的值是undefined,那么后面的表達式將會被忽略,不會引發異常
<p>{{ detail?.telNum }}</p>

//星號前綴:使用星號前綴可以簡化對結構指令的使用,Angular會將帶有星號的指令引用替換成帶有<template>標簽的代碼,
//等價於<template [myUnless]="boolValue"><p>myUnless is false now.</p></template>
<p *myUnless="boolValue">myUnless is false now.</p>

 

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

2 數據綁定

根據數據流向可以分為三種:

  • 單向:數據源到視圖
//插值DOM元素屬性
<p>{{ detail.telNum }}</P>

//綁定HTML標簽特性
<div [title]="name"></div>

//綁定
<div [style.color]="color">hello world</div>
  • 單向:從視圖目標到數據源
//事件綁定
(click)="editContact()"
on-click="editContact()"
  • 雙向
//雙向綁定
<div [(title)]="name"></div> <div bindon-title="name"></div>
  • 1
  • 2
  • 3

NOTE 
Property為DOM對象屬性,以DOM元素作為多想,其附加內容,是在文檔對象模型里定義的,如childNodes、firstChild。 而Attribute為HTML標簽特性,是DOM節點自帶的屬性,在HTML中定義的,即只要是在HTML標簽中出現的屬性(HTML代碼)都是Attribute。

數據綁定是借助於DOM對象屬性和事件來運作的。

2.1 插值

雙大括號{{ }}語法來實現。

2.2 模板表達式

類似於JS的表達式,絕大多數JS表達式均為合法模板表達式。它應用於插值語法雙大括號中和屬性綁定“=”右側的括號中。但以下JS表達式不是合法模板表達式:

  • 帶有new運算符的表達式
  • 賦值表達式
  • 帶有 ; 或者 , 的鏈式表達式
  • 帶有自增自減

模板表達式不支持位運算。

2.3 屬性綁定

DOM元素屬性綁定:把DOM對象屬性綁定到組件的屬性上,而綁定目標可以是中括號,也可以加前綴,還可以使用屬性綁定設置自定義組件的輸入屬性。

//中括號
<div [title]="titleText"></div>

//加前綴
<div bind-title="titleText"></div>

//自定義組件的輸入屬性
<user-detail [user]="currentUser"></user-detail> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

NOTE: 
中括號的作用是計算右側模板表達式,如果沒有中括號,右側表達式會被當成字符串常量,如果是字符串常量則建議省略中括號,例如:

<user-detail detail="我是字符串常量" [user]="currentUser"></user-detail>
  • 1

HTML標簽特性綁定:純粹的HTML標簽特性比如<table>colspan采用和DOM一樣的綁定方式會報錯,例如:

//以下模板會出現解析錯誤
<table>
  <tr>
    <td colspan="{{ 1 + 2 }}"></td>
  </tr>
</table>

//正確的HTML標簽特性綁定
<table>
  <tr>
    <td [attr.colspan]="{{ 1 + 2 }}"></td>
  </tr>
</table> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

HTML標簽特性綁定類似於屬性綁定,但是中括號的部分不是一個元素的屬性名,而是由attr.前綴和HTML元素特性名稱組成的形式。

CSS類綁定:CSS類既屬於DOM對象屬性,也屬於HTML標簽特性,所以可以使用以上兩種方式綁定:

<div class='red font14' [class]="changeGreen">14號 綠色字</div>
  • 1

特有的綁定方式:[class.class-name]語法,被賦值為true時,將class-name這個類添加到該綁定的標簽上,否則移除這個類。

<div [class.class-blue]="isBlue()">若isBlue()返回true,這里的字體將變成藍色</div>

<div class="footer" [class.footer]="showFooter">若showFooter為false,則footer這個css被移除</div>
  • 1
  • 2
  • 3

Style樣式綁定:HTML標簽內聯樣式可以通過Style樣式綁定的方式設置。語法為[style.style-property],可以帶單位如px和%:

<button [style.background-color]="canClick ? 'blue' : 'red'">若canClick為true,則按鈕背景顏色為藍色</button>

<button [style.font-size.px]="isLarge ? 18 : 13">若isLarge為true,則按鈕字體變為18px</button>
  • 1
  • 2
  • 3

2.4 事件綁定

單向,數據從模板到組件類流動。Angular監聽用戶操作時間,如鍵盤事件、鼠標事件、觸屏事件等方法。事件綁定的語法為:“=”左側小括號內的目標事件和“=”右側引號中的模板語句組成。

模板語句與模板表達式一樣,和JS表達式類似,有一些JS表達式在模板語句中不被支持:

  • 賦值操作,如+=或-=
  • 自增和自減操作符(++和–)
  • new 操作符
  • 位運算符 | 和 &
  • 模板表達式運算符

模板語句和模板運算符一樣,只能訪問其上下文環境的成員,模板語句的上下文環境就是綁定事件對應組件的實例。也可以包含組件外的對象,如模板局部變量和事件綁定語句中的$event

目標事件:小括號中的事件名表示目標事件,還可以帶on-前綴的形式來標記目標事件,還可以是自定義指令的事件:

<a class="edit" (click)="editContact()"></a>

<a class="edit" on-click="editContact()"></a>

<a class="edit" (myClick)="editContact=$event"></a>
  • 1
  • 2
  • 3
  • 4
  • 5

$event事件對象$event事件對象用來獲取事件的相關信息,如果目標事件是原生DOM元素事件(可以是自定義事件),則$event將是一個包含了targettarget.value屬性的DOM時間對象,例如:

<input [value]="currentUser.firstName" (input)="currentUser.firstName=$event.target.value"/>

  • 1

自定義事件:借助EventEmitter實現。它的實現步驟:一、在組件中創建EventEmitter實例對象,並將其以輸出屬性形式暴露;二、父組件通過綁定一個屬性來自定義一個事件;三、在組件中調用EventEmitter.emit()觸發自定義事件;四、父組件綁定的事件通過$event對象訪問數據。

//父組件collection.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'collection',
  template: `<contact-collect [contact]="detail" (onCollect)="collectTheContact($event)"></contact-collect>`
})

export class CollectionComponent implements OnInit{
  detail: any = {};
  collectTheContact(){
    this.detail.collection == 0 ? this.detail.collection = 1 : this.detail.collection = 0;
  }
}

//子組件contactCollect.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'contact-collect',
  template: `<i [ngClass]="{collected: contact.collection}" (click)="collectTheContact()">收藏</i>`
})

export class CollectionComponent implements OnInit{
  @Input() contact: any = {};
  @Output() onCollect = new EventEmitter<boolean>();
  collectTheContact(){
    this.onCollect.emit();
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

子組件click事件觸發collectTheContact方法,該方法調用EventEmitter實例化對象onCollect()emit方法,向父組件發送數據;父組件綁定了子組件的onCollect事件,該事件被觸發后將調用父元素的collectTheContact($event)方法,並以$event訪問數據。

2.5 雙向數據綁定

//最原始的實現方式
<input [value]="currentUser.firstName" (input)="currentUser.firstName=$event.target.value"/>

//借助於NgModel,展開形式
<input [ngModel]="currentUser.phoneNumber" (ngModelChange)="currentUser.phoneNumver=$event"/>

//最簡潔的方式
<input [(ngModel)]="currentUser.phoneNumber"/> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

[ ]實現了數據流從組件類到模板,( )實現了數據流從模板到組件類。

2.6 輸入輸出屬性

綁定聲明中,“=”左側的稱為綁定目標,“=”右側稱為綁定源。

//list.component.html
<list-item [contact]="contact" (routerNavigate)="routerNavigate($event)"></list-item>
  • 1
  • 2

list-item中,數據通過模板表達式流向目標屬性contact,因而contactListComponent中是一個輸入屬性。而事件綁定中,數據流向routerNavigate綁定源,傳遞給接收者,routerNavigate是一個輸出屬性。

綁定目標必須被明確地標記為輸入或輸出屬性,可以以修飾符(@Input@Output)或組件元數據(inputsoutputs)兩種方式聲明。

//goto是別名
@Output('goto') clicks = new EventEmitter<number>();

//元數據方式
@Component({
  outputs: ['clicks:goto']
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

3 內置指令

NgClass:通過它,可以動態添加或移除多個類。NgClass綁定一個由CSS類名:value的對象,value是一個布爾類型的數據值,當valuetrue時添加對應的類名到模板元素中,反之刪除。

setClasses(){
  let classes={
    red: this.red,
    font14: !this.font14,
    title: this.isTitle
  }
  return classes
}

<div [ngClass]="setClass()"></div> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

NgStyle:設置多個內聯樣式。綁定刑如CSS屬性名:value的對象。

setStyles(){
  let styles = {
    'color': this.red ? 'red' : 'blue',
    'font-size': !this.font14 ? '14px' : '16px',
    'font-weight': this.isSpecial ? 'bold' : 'normal' 
  };
  return styles;
}

<div [ngStyle]="setStyles"></div> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

NgIf:綁定一個布爾類型的表達式,當表達式真時候,DOM樹節點上添加一個元素及其子元素,否則移除(查看DOM樹不能看到該元素)。

<div *ngIf="collect.length === 0"></div>
  • 1

NgStitch:根據NgSwitch綁定的模板表達式返回值決定添加那個模板元素到DOM節點上。

<span [ngSwitch]="contactName">
  <span *ngSwitchCase="'TimCook'">蒂姆·庫克</span>
  <span *ngSwitchCase="'BillGates'">比爾蓋茨</span>
  <span *ngSwitchDefault>無名氏</span>
</span> 
  • 1
  • 2
  • 3
  • 4
  • 5

NgFor:重復執行某些步驟來展現數據,它支持一個可選的index索引,下標范圍為0<=index<數組的長度。

<div *ngFor="let contact of contacts;let i=index">{{i + 1}} - {{ contact.id }}</div>
  • 1

NgForTrackBy:每次更改都會引發很多相關聯的DOM操作,使用NgFor會讓性能變得很差,比如重新從服務器拉取列表數據,雖然大部分數據沒變化,但是因為不知道哪些數據變化了,需要清空並重新渲染。可以通過使用追蹤函數避免重復渲染的性能浪費。

trackByContacts(index: number, contact: Contact){
  return contact.id;
}

<div *ngFor="let contact of contacts; trackBy: trackByContacts">{{ contact.id }}</div> 
  • 1
  • 2
  • 3
  • 4
  • 5

4 表單

HTML內置表單標簽一些特性存在瀏覽器兼容性問題,在自定義規則、表單數據獲取、處理、提交等流程都比較復雜。Angualr提供了雙向數據綁定、強大的檢驗規則以及自定義檢驗錯誤提示等功能。Angular提供了模板驅動(使用模板表單內置指令、內置檢驗方式)和模型驅動(自定義)兩種方式構建表單。

4.1 一個模板表單例子

@Component{
  selector: 'add-content',
  template: `
    <h3>添加聯系人</h3>
    <form>
      <ul>
        <li>
          <label for="name">姓名:</label>
          <input type='text' name='name'/>
        </li>
        <!--...-->
        <li>
          <button type='submit'>添加</button>
          <button type='button'>取消</button>
        </li>
      </ul>
    </form>
  `
}

export class FormComponent {} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

4.2 表單指令

NgForm:表單控制中心,負責處理表單頁面邏輯,擴展了額外表單特性,表單指令在NgForm指令內部才能正常運行。

NgForm的使用步驟如下:

  1. 在根模塊導入FormsModule模塊和FormComponent組件
  2. FormComponent組件中直接使用NgForm
import { NgModule } from '@angular/core';
import { BrowserModule} from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent} from './app.component';
import { FormComponent} from './form.component';

@NgModule({
  imports:[
    BrowserModule,
    FormsModule
  ],
  declarations:[
    AppComponent,
    FormComponent
  ]
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

NgModelNgModel實現了表單控件的數據綁定,提供了控件狀態跟蹤和檢驗功能。

<input type='text' name='contactName' [(ngModel)]="curContact.name"/>
  • 1

控件中使用NgModel,必須添加name屬性,否則報錯。原因是,NgForm指令為表單建立一個控件對象FormControl的集合,以此來作為表單控件的容器。控件的NgModel屬性綁定會以name作為唯一標識來注冊並生成一個FormControl,並將其加入到FormControl的集合中。

單選框NgModel會綁定選中的單選框的值

<input type='radio' name="sex" [(ngModel)]="curContact.sex" value="female" />女
<input type='radio' name="sex" [(ngModel)]="curContact.sex" value="male" />男
  • 1
  • 2

復選框NgModel會綁定一個布爾值

<input type='checkbox' name="lock" [(ngModel)]="curContact.lock" />
  • 1

單選下拉框option綁定目標有兩種,valuengValuevalue返回值類型為基本數據類型,ngValue返回值為對象數據類型。

//第一步:定義下拉框列表所需的數據
export class FormComponent {
  interests:any[] = [
    {value: 'reading', display: '閱讀'},
    {value: 'traveling', display: '旅游'},
    {value: 'sport', display: '運動'}
  ]
}

//第二步:構建下拉框模板
<select name="interestValue" [(ngModel)]="curContact.interestValue">
  <option *ngFor="let interst of intersts" [value]="interest.value">
    {{interest.display}}
  </option>
</select> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

多選下拉框: 與單選下拉框類似,不過返回值為選中數據的數組。

模板局部變量:模板中對DOM元素或指令(包括組件)的引用(作用類似於getElementById),可以使用在當前元素、兄弟元素或任何子元素中。

DOM元素局部變量:局部變量名前加#符號或者加ref-前綴

<input type='text' #contactName name="contactName" id="contactName"/>
<input type='number' ref-telNum name="telNum" id="telNum"/>
  • 1
  • 2

表單指令局部變量:表單指令的局部變量在定義時需手動初始化為特定指令的代表值,最終解析后會被賦值為表單指令實例對象的引用。

  • NgForm表單局部變量
<form #contactForm="ngForm">
  //...
</form>
  • 1
  • 2
  • 3

局部變量#contactFormNgForm指令實例對象的引用,可以在模板中讀取NgForm實例對象的屬性值,如追蹤表單的valid屬性狀態。

  • NgModel控件局部變量
<input type='text' name="contactName" [(ngModel)]="curContact.name" #contactName="ngModel"/> <p>{{ contactName.valid }}</p>
  • 1
  • 2

局部變量contactNameNgModel指令實例對象的引用,可以通過它讀取NgModel的屬性值。

表單狀態NgFormNgModel指令都可以用於追蹤表單狀態來實現數據檢驗,他們都有五個表示狀態的屬性,屬性值為布爾類型,可通過對應的局部變量來獲取。NgForm追蹤的是整個表單控件的狀態,NgModel追蹤單個控件。

  • valid:表單值是否改變
  • pristine:表單值是否未改變
  • dirty:表單值是否已改變
  • touched:表單是否已被訪問過
  • untouched:表單時是否未被訪問過

表單狀態檢驗有三個時段,初始狀態、輸入后狀態(validpristinedirty狀態改變)、失去焦點后狀態(toucheduntouched狀態改變)。

NgModelGroup指令:對表單輸入內容進行分組,方便在語義上區分不同類型的輸入。

<fieldset ngModelGroup="nameGroup" #nameGroup="ngModelGroup">
  <label>姓:</label>
  <input type='text' name="firstName" [(ngModel)]="curContact.firstName" required/>
  <label>名:</label>
  <input type='text' name="lastName" [(ngModel)]="curContact.lastName" required/>
</fieldset> 
1
2
3
4
5
6
//這是form中的數據格式
{
  nameGroup: {
    firstName: '',
    lastName: ''
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

ngSubmit事件

<form #contactForm="ngForm" (ngSubmit)="doSubmit(contactForm.value)">
  <li>
    <button type='submit' [disabled]="!contactForm.value">
      添加
    </button>
    <button type='reset'>重置</button>
  </li>
</form>

export class FormComponent{
  doSubmit(formValue: any){

  }
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

4.3 自定義表單樣式

.ng-valid[required] {
  border-left: 5px solid #0f0;
}
.ng-invalid {
  border-left: 5px solid #f00;
}

<p [hidden]="contactName.valid || contactName.pristine">用戶名長度為3-10個字符</p> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

4.4 表單檢驗

表單內置檢驗requiredminlengthmaxlengthpattern

表單自定義檢驗

  • 創建自定義檢驗
//validate-username.ts
import { FormControl } from '@angular/forms';

const EMAIL_REGEXP = new RegExp("[a-z0-9]+@[a-z0-9]+.com");
const TEL_REGEXP = new RegExp("1[0-9]{10}");

export function validateUserName(c: FormControl) {
  return (EMAIL_REGEXP.test(c.value) || TEL_REGEXP.test(c.value)) ? null : {
    userName: {
      valid: false,
      errorMsg: '用戶名必須是手機號或者郵箱賬號'
    }
  };
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 使用自定義檢驗
//...
import { ReactiveFormsModule } from '@angular/forms';
import { FormComponent } from './form.component';
import { AppComponent} from './app.component';

@NgModule({
  imports: [BrowserModule, ReactiveFormsModule],
  declarations: [AppComponent, FormComponent],
  bootstrap: [AppComponent]
})

export class AppModule {} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

首先要導入ReactiveFormsModule

import { Component } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
import { validateUserName } from './validate-username';

@Component({
  selector: 'add-contact',
  template: `
   <form [formGroup]="customForm">
     <label>姓名:</label>
     <input type='text' formControlName='customName'/>
   </form>
  `
})

export class FormComponent{
  customForm = new FormGroup({
    customName: new FormControl('', validateUserName)
  });
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

分別定義了FormGroupFormControl的實例化對象

5 管道

Angular中,管道可以按照開發者指定的規則將模板內的數據進行轉換。

5.1 什么是管道

模板中,通過管道操作符 | 使用管道,| 左邊的為輸入數 據,右邊為管道。管道可以帶有參數,通過傳入參數輸出不同格式數據。同時,模板表達式中可以同時使用多個管道進行不同的處理。

<p>{{ birthday | date }}</p>

<P>{{ birthday | data:"MM/dd/y" }}</p>

<p>{{ expression | pipeName1 | pipeName2 }}</p> 
  • 1
  • 2
  • 3
  • 4
  • 5

5.2 幾種管道

內置管道:Angular提供的,不需導入可以直接使用。

  • DatePipe:日期管道,格式化日期,純管道
  • JsonPipe:將輸入數據對象經過JSON.stringify()方法轉換后輸出對象字符串,非純管道
  • UpperCasePipe:文本中所有小寫字母全轉換為字母,純
  • LowerCasePipe:變成小寫,純
  • DecimalPipe:將數值按特定格式顯示文本,純
  • CurrencyPipe:數值轉化為本地貨幣格式,純
  • PercentPipe:數值轉百分比,純
  • SlicePipe:將數值或者字符串裁剪成新的子集,非純管道
expression | date: format

expression | json

expression | uppercase

expression | lowercase

expression | number[: digitInfo]

expression | currency[: currencyCode[: symbolDisplay[: digitInfo]]]

expression | percent

expression | slice: start[: end] 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

自定義管道:通過以下幾個步驟實現

  • 定義元數據:引入Pipe和PipeTransform,同時為管道命名
//sexreform.pipe.ts
import { Pipe, PipeTransform } from "@angular/core";

@Pipe {
  name: 'sexReform'
}

export class SexReform implements PipeTransform {
  //...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 實現transform方法
export class SexReform implements PipeTransform {
  transform(val: string): string {
    switch(val) {
      case 'male': return '男';
      case 'female' return '女';
      default: return '未知性別';
    }
  }
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 使用自定義管道
//使用管道前,需要在@NgModule的元數據declarations數組中添加自定義管道
import { SexReform } from 'pipes/sexreform.pipe';

@NgModule ({
  //...
  declarations: [SexReform]
})


//可以像內置管道一般使用自定義管道咯
@Component ({
  selector: 'pipe-demo-custom',
  template: `
    <p>{{ sexValue | sexReform }}</p>
  `
}) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

純管道與非純管道的區別:只有發生純變才會調用該管道,這類管道稱為純管道,其余的管道成為非純管道。純變指的是對基本數據類型(String、Number、Boolean)輸入值的變更或者對對象引用(Array、Function、Object)的更改。

只要數據發生改變,均會觸發非純管道,但不一定會觸發純管道,需要考察數據變化的情形是否為純變化。看下面這個例子:

//...
@Component ({
  selector: 'pure-pipe-demo',
  template: `
    <div>
      <p>{{ dateObj | date: "y-MM-dd HH:mm:ss EEEE" }}</p>
      <p>{{ dateStr | date: "y-MM-dd HH:mm:ss EEEE" }}</p>
    </div>
  `
})

export class PurePipeDemoComponent {
  dateObj: date = new Date('2016-06-08 20:05:08');
  dateStr: string = '2016-06-08 20:05:08';

  constructor(){
    setTimeout(() => {
    this.dateObj.setMonth(11),
    this.dateStr = '2016-12-08 20:05:08'
    },2000);
  }
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

初始日期分別為:

'2016-06-08 20:05:08 Wednesday'
'2016-06-08 20:05:08 Wednesday'
  • 1
  • 2

2s之后變成了

'2016-06-08 20:05:08 Wednesday'
'2016-12-08 20:05:08 Thursday'
  • 1
  • 2

1 什么是模板

它是頁面呈現的骨架,是盛裝組件數據的容器。與之相關的內容包括了模板與組件件數據交互、內置指令、表單、管道等。

1.1 模板語法概覽

//插值:綁定屬性變量的值到模板中
<p>{{ detail.telNum }}</p>

//DOM元素屬性綁定:將模板表達式name的值綁定到元素<div>的屬性title上
<div [title]="name"></div>

//HTML標簽特性綁定:將模板表達式的返回值綁定到元素<td>標簽特性colspan上
<td [attr.colspan]="{{ 1+2 }}">合並單元格</td>

//Class類綁定:當isBlue()函數值為true時為div添加類名為isBlue的樣式
<div [class.isblue]="isBlue()"></div>

//Style樣式綁定:當表達式isRed的值為為true時設置button的文字顏色為紅色,否則為綠色
<button [style.color]="isRed ? 'red':'green'">紅色</button>

//事件綁定:單擊元素時會觸發click事件,需要時也可以傳遞$event對象,如(click)="editContact($event)"
<a class='edit' (click)="editContact()"></a>

//雙向綁定:組件和模板間雙向數據綁定,等價於<div [title]="name" (titleChange)="name=$event"></div>
<div [(title)]="name"></div>

//模板局部變量:在當前模板中創建一個對id值為name的input元素的引用變量name,相當於document.getElementById("name")
<input type='text' ##name name="name" id="name"/>

//管道操作符:原始數據birthday經管道轉換后輸出期望數據並顯示在模板中
<p>張三的生日是{{ birthday | date }}</p>

//模板表達式操作符:模板表達式操作符表明detail.telNum屬性不是必須存在的,如果它的值是undefined,那么后面的表達式將會被忽略,不會引發異常
<p>{{ detail?.telNum }}</p>

//星號前綴:使用星號前綴可以簡化對結構指令的使用,Angular會將帶有星號的指令引用替換成帶有<template>標簽的代碼,等價於<template [myUnless]="boolValue"><p>myUnless is false now.</p></template>
<p *myUnless="boolValue">myUnless is false now.</p> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

2 數據綁定

根據數據流向可以分為三種:

  • 單向:數據源到視圖
//插值DOM元素屬性
<p>{{ detail.telNum }}</P>

//綁定HTML標簽特性
<div [title]="name"></div>

//綁定
<div [style.color]="color">hello world</div> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 單向:從視圖目標到數據源
//事件綁定
(click)="editContact()"
on-click="editContact()"
  • 1
  • 2
  • 3
  • 雙向
//雙向綁定
<div [(title)]="name"></div>
<div bindon-title="name"></div>
  • 1
  • 2
  • 3

NOTE 
Property為DOM對象屬性,以DOM元素作為多想,其附加內容,是在文檔對象模型里定義的,如childNodes、firstChild。 而Attribute為HTML標簽特性,是DOM節點自帶的屬性,在HTML中定義的,即只要是在HTML標簽中出現的屬性(HTML代碼)都是Attribute。

數據綁定是借助於DOM對象屬性和事件來運作的。

2.1 插值

雙大括號{{ }}語法來實現。

2.2 模板表達式

類似於JS的表達式,絕大多數JS表達式均為合法模板表達式。它應用於插值語法雙大括號中和屬性綁定“=”右側的括號中。但以下JS表達式不是合法模板表達式:

  • 帶有new運算符的表達式
  • 賦值表達式
  • 帶有 ; 或者 , 的鏈式表達式
  • 帶有自增自減

模板表達式不支持位運算。

2.3 屬性綁定

DOM元素屬性綁定:把DOM對象屬性綁定到組件的屬性上,而綁定目標可以是中括號,也可以加前綴,還可以使用屬性綁定設置自定義組件的輸入屬性。

//中括號
<div [title]="titleText"></div>

//加前綴
<div bind-title="titleText"></div>

//自定義組件的輸入屬性
<user-detail [user]="currentUser"></user-detail> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

NOTE: 
中括號的作用是計算右側模板表達式,如果沒有中括號,右側表達式會被當成字符串常量,如果是字符串常量則建議省略中括號,例如:

<user-detail detail="我是字符串常量" [user]="currentUser"></user-detail>
  • 1

HTML標簽特性綁定:純粹的HTML標簽特性比如<table>colspan采用和DOM一樣的綁定方式會報錯,例如:

//以下模板會出現解析錯誤 <table> <tr> <td colspan="{{ 1 + 2 }}"></td> </tr> </table> //正確的HTML標簽特性綁定 <table> <tr> <td [attr.colspan]="{{ 1 + 2 }}"></td> </tr> </table>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

HTML標簽特性綁定類似於屬性綁定,但是中括號的部分不是一個元素的屬性名,而是由attr.前綴和HTML元素特性名稱組成的形式。

CSS類綁定:CSS類既屬於DOM對象屬性,也屬於HTML標簽特性,所以可以使用以上兩種方式綁定:

<div class='red font14' [class]="changeGreen">14號 綠色字</div>
  • 1

特有的綁定方式:[class.class-name]語法,被賦值為true時,將class-name這個類添加到該綁定的標簽上,否則移除這個類。

<div [class.class-blue]="isBlue()">若isBlue()返回true,這里的字體將變成藍色</div>

<div class="footer" [class.footer]="showFooter">若showFooter為false,則footer這個css被移除</div>
  • 1
  • 2
  • 3

Style樣式綁定:HTML標簽內聯樣式可以通過Style樣式綁定的方式設置。語法為[style.style-property],可以帶單位如px和%:

<button [style.background-color]="canClick ? 'blue' : 'red'">若canClick為true,則按鈕背景顏色為藍色</button>

<button [style.font-size.px]="isLarge ? 18 : 13">若isLarge為true,則按鈕字體變為18px</button>
  • 1
  • 2
  • 3

2.4 事件綁定

單向,數據從模板到組件類流動。Angular監聽用戶操作時間,如鍵盤事件、鼠標事件、觸屏事件等方法。事件綁定的語法為:“=”左側小括號內的目標事件和“=”右側引號中的模板語句組成。

模板語句與模板表達式一樣,和JS表達式類似,有一些JS表達式在模板語句中不被支持:

  • 賦值操作,如+=或-=
  • 自增和自減操作符(++和–)
  • new 操作符
  • 位運算符 | 和 &
  • 模板表達式運算符

模板語句和模板運算符一樣,只能訪問其上下文環境的成員,模板語句的上下文環境就是綁定事件對應組件的實例。也可以包含組件外的對象,如模板局部變量和事件綁定語句中的$event

目標事件:小括號中的事件名表示目標事件,還可以帶on-前綴的形式來標記目標事件,還可以是自定義指令的事件:

<a class="edit" (click)="editContact()"></a>

<a class="edit" on-click="editContact()"></a>

<a class="edit" (myClick)="editContact=$event"></a>
  • 1
  • 2
  • 3
  • 4
  • 5

$event事件對象$event事件對象用來獲取事件的相關信息,如果目標事件是原生DOM元素事件(可以是自定義事件),則$event將是一個包含了targettarget.value屬性的DOM時間對象,例如:

<input [value]="currentUser.firstName" (input)="currentUser.firstName=$event.target.value"/>
  • 1

自定義事件:借助EventEmitter實現。它的實現步驟:一、在組件中創建EventEmitter實例對象,並將其以輸出屬性形式暴露;二、父組件通過綁定一個屬性來自定義一個事件;三、在組件中調用EventEmitter.emit()觸發自定義事件;四、父組件綁定的事件通過$event對象訪問數據。

//父組件collection.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'collection',
  template: `
    <contact-collect [contact]="detail" (onCollect)="collectTheContact($event)"></contact-collect>
  `
})

export class CollectionComponent implements OnInit{
  detail: any = {};
  collectTheContact(){
    this.detail.collection == 0 ? this.detail.collection = 1 : this.detail.collection = 0;
  }
}


//子組件contactCollect.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'contact-collect',
  template: `
    <i [ngClass]="{collected: contact.collection}" (click)="collectTheContact()">收藏</i>
  `
})

export class CollectionComponent implements OnInit{
  @Input() contact: any = {};
  @Output() onCollect = new EventEmitter<boolean>();
  collectTheContact(){
    this.onCollect.emit();
  }
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

子組件click事件觸發collectTheContact方法,該方法調用EventEmitter實例化對象onCollect()emit方法,向父組件發送數據;父組件綁定了子組件的onCollect事件,該事件被觸發后將調用父元素的collectTheContact($event)方法,並以$event訪問數據。

2.5 雙向數據綁定

//最原始的實現方式
<input [value]="currentUser.firstName" (input)="currentUser.firstName=$event.target.value"/>

//借助於NgModel,展開形式
<input [ngModel]="currentUser.phoneNumber" (ngModelChange)="currentUser.phoneNumver=$event"/>

//最簡潔的方式
<input [(ngModel)]="currentUser.phoneNumber"/> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

[ ]實現了數據流從組件類到模板,( )實現了數據流從模板到組件類。

2.6 輸入輸出屬性

綁定聲明中,“=”左側的稱為綁定目標,“=”右側稱為綁定源。

//list.component.html
<list-item [contact]="contact" (routerNavigate)="routerNavigate($event)"></list-item>
  • 1
  • 2

list-item中,數據通過模板表達式流向目標屬性contact,因而contactListComponent中是一個輸入屬性。而事件綁定中,數據流向routerNavigate綁定源,傳遞給接收者,routerNavigate是一個輸出屬性。

綁定目標必須被明確地標記為輸入或輸出屬性,可以以修飾符(@Input@Output)或組件元數據(inputsoutputs)兩種方式聲明。

//goto是別名
@Output('goto') clicks = new EventEmitter<number>();

//元數據方式
@Component({
  outputs: ['clicks:goto']
}) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

3 內置指令

NgClass:通過它,可以動態添加或移除多個類。NgClass綁定一個由CSS類名:value的對象,value是一個布爾類型的數據值,當valuetrue時添加對應的類名到模板元素中,反之刪除。

setClasses(){
  let classes={
    red: this.red,
    font14: !this.font14,
    title: this.isTitle
  }
  return classes
}


<div [ngClass]="setClass()"></div> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

NgStyle:設置多個內聯樣式。綁定刑如CSS屬性名:value的對象。

setStyles(){
  let styles = {
    'color': this.red ? 'red' : 'blue',
    'font-size': !this.font14 ? '14px' : '16px',
    'font-weight': this.isSpecial ? 'bold' : 'normal' 
  };
  return styles;
}
<div [ngStyle]="setStyles"></div> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

NgIf:綁定一個布爾類型的表達式,當表達式真時候,DOM樹節點上添加一個元素及其子元素,否則移除(查看DOM樹不能看到該元素)。

<div *ngIf="collect.length === 0"></div>
  • 1

NgStitch:根據NgSwitch綁定的模板表達式返回值決定添加那個模板元素到DOM節點上。

<span [ngSwitch]="contactName">
  <span *ngSwitchCase="'TimCook'">蒂姆·庫克</span>
  <span *ngSwitchCase="'BillGates'">比爾蓋茨</span>
  <span *ngSwitchDefault>無名氏</span>
</span>
  • 1
  • 2
  • 3
  • 4
  • 5

NgFor:重復執行某些步驟來展現數據,它支持一個可選的index索引,下標范圍為0<=index<數組的長度。

<div *ngFor="let contact of contacts;let i=index">{{i + 1}} - {{ contact.id }}</div>
  • 1

NgForTrackBy:每次更改都會引發很多相關聯的DOM操作,使用NgFor會讓性能變得很差,比如重新從服務器拉取列表數據,雖然大部分數據沒變化,但是因為不知道哪些數據變化了,需要清空並重新渲染。可以通過使用追蹤函數避免重復渲染的性能浪費。

trackByContacts(index: number, contact: Contact){
 return contact.id;
}

<div *ngFor="let contact of contacts; trackBy: trackByContacts">{{ contact.id }}</div>
  • 1
  • 2
  • 3
  • 4
  • 5

4 表單

HTML內置表單標簽一些特性存在瀏覽器兼容性問題,在自定義規則、表單數據獲取、處理、提交等流程都比較復雜。Angualr提供了雙向數據綁定、強大的檢驗規則以及自定義檢驗錯誤提示等功能。Angular提供了模板驅動(使用模板表單內置指令、內置檢驗方式)和模型驅動(自定義)兩種方式構建表單。

4.1 一個模板表單例子

@Component{
  selector: 'add-content',
  template: `
    <h3>添加聯系人</h3>
    <form>
      <ul>
        <li>
          <label for="name">姓名:</label>
          <input type='text' name='name'/>
        </li>
        <!--...-->
        <li>
          <button type='submit'>添加</button>
          <button type='button'>取消</button>
        </li>
      </ul>
    </form>
  `
}
export class FormComponent {} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

4.2 表單指令

NgForm:表單控制中心,負責處理表單頁面邏輯,擴展了額外表單特性,表單指令在NgForm指令內部才能正常運行。

NgForm的使用步驟如下:

  1. 在根模塊導入FormsModule模塊和FormComponent組件
  2. FormComponent組件中直接使用NgForm
import { NgModule } from '@angular/core';
import { BrowserModule} from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent} from './app.component';
import { FormComponent} from './form.component';

@NgModule({
  imports:[
    BrowserModule,
    FormsModule
  ],
  declarations:[
    AppComponent,
    FormComponent
  ]
}) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

NgModelNgModel實現了表單控件的數據綁定,提供了控件狀態跟蹤和檢驗功能。

<input type='text' name='contactName' [(ngModel)]="curContact.name"/>
  • 1

控件中使用NgModel,必須添加name屬性,否則報錯。原因是,NgForm指令為表單建立一個控件對象FormControl的集合,以此來作為表單控件的容器。控件的NgModel屬性綁定會以name作為唯一標識來注冊並生成一個FormControl,並將其加入到FormControl的集合中。

單選框NgModel會綁定選中的單選框的值

<input type='radio' name="sex" [(ngModel)]="curContact.sex" value="female" />女
<input type='radio' name="sex" [(ngModel)]="curContact.sex" value="male" />男
  • 1
  • 2

復選框NgModel會綁定一個布爾值

<input type='checkbox' name="lock" [(ngModel)]="curContact.lock" />
  • 1

單選下拉框option綁定目標有兩種,valuengValuevalue返回值類型為基本數據類型,ngValue返回值為對象數據類型。

//第一步:定義下拉框列表所需的數據
export class FormComponent {
  interests:any[] = [
    {value: 'reading', display: '閱讀'},
    {value: 'traveling', display: '旅游'},
    {value: 'sport', display: '運動'}
  ]
}

//第二步:構建下拉框模板
<select name="interestValue" [(ngModel)]="curContact.interestValue">
  <option *ngFor="let interst of intersts" [value]="interest.value">
    {{interest.display}}
  </option>
</select> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

多選下拉框: 與單選下拉框類似,不過返回值為選中數據的數組。

模板局部變量:模板中對DOM元素或指令(包括組件)的引用(作用類似於getElementById),可以使用在當前元素、兄弟元素或任何子元素中。

DOM元素局部變量:局部變量名前加#符號或者加ref-前綴

<input type='text' #contactName name="contactName" id="contactName"/>
<input type='number' ref-telNum name="telNum" id="telNum"/>
  • 1
  • 2

表單指令局部變量:表單指令的局部變量在定義時需手動初始化為特定指令的代表值,最終解析后會被賦值為表單指令實例對象的引用。

  • NgForm表單局部變量
<form #contactForm="ngForm">
  //...
</form>
  • 1
  • 2
  • 3

局部變量#contactFormNgForm指令實例對象的引用,可以在模板中讀取NgForm實例對象的屬性值,如追蹤表單的valid屬性狀態。

  • NgModel控件局部變量
<input type='text' name="contactName" [(ngModel)]="curContact.name" #contactName="ngModel"/> <p>{{ contactName.valid }}</p>
  • 1
  • 2

局部變量contactNameNgModel指令實例對象的引用,可以通過它讀取NgModel的屬性值。

表單狀態NgFormNgModel指令都可以用於追蹤表單狀態來實現數據檢驗,他們都有五個表示狀態的屬性,屬性值為布爾類型,可通過對應的局部變量來獲取。NgForm追蹤的是整個表單控件的狀態,NgModel追蹤單個控件。

  • valid:表單值是否改變
  • pristine:表單值是否未改變
  • dirty:表單值是否已改變
  • touched:表單是否已被訪問過
  • untouched:表單時是否未被訪問過

表單狀態檢驗有三個時段,初始狀態、輸入后狀態(validpristinedirty狀態改變)、失去焦點后狀態(toucheduntouched狀態改變)。

NgModelGroup指令:對表單輸入內容進行分組,方便在語義上區分不同類型的輸入。

<fieldset ngModelGroup="nameGroup" #nameGroup="ngModelGroup">
  <label>姓:</label>
  <input type='text' name="firstName" [(ngModel)]="curContact.firstName" required/>
  <label>名:</label>
  <input type='text' name="lastName" [(ngModel)]="curContact.lastName" required/>
</fieldset> 
1
2
3
4
5
6
//這是form中的數據格式
{
  nameGroup: {
    firstName: '',
    lastName: ''
  }
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

ngSubmit事件

<form #contactForm="ngForm" (ngSubmit)="doSubmit(contactForm.value)">
  <li>
    <button type='submit' [disabled]="!contactForm.value">
      添加
    </button>
    <button type='reset'>重置</button>
  </li>
</form>

export class FormComponent{
  doSubmit(formValue: any){

  }
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

4.3 自定義表單樣式

.ng-valid[required] {
  border-left: 5px solid #0f0;
}
.ng-invalid {
  border-left: 5px solid #f00;
}

<p [hidden]="contactName.valid || contactName.pristine">用戶名長度為3-10個字符</p> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

4.4 表單檢驗

表單內置檢驗requiredminlengthmaxlengthpattern

表單自定義檢驗

  • 創建自定義檢驗
//validate-username.ts
import { FormControl } from '@angular/forms';

const EMAIL_REGEXP = new RegExp("[a-z0-9]+@[a-z0-9]+.com");
const TEL_REGEXP = new RegExp("1[0-9]{10}");

export function validateUserName(c: FormControl) {
  return (EMAIL_REGEXP.test(c.value) || TEL_REGEXP.test(c.value)) ? null : {
    userName: {
      valid: false,
      errorMsg: '用戶名必須是手機號或者郵箱賬號'
    }
  };
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 使用自定義檢驗
//...
import { ReactiveFormsModule } from '@angular/forms';
import { FormComponent } from './form.component';
import { AppComponent} from './app.component';

@NgModule({
  imports: [BrowserModule, ReactiveFormsModule],
  declarations: [AppComponent, FormComponent],
  bootstrap: [AppComponent]
})

export class AppModule {} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

首先要導入ReactiveFormsModule

import { Component } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
import { validateUserName } from './validate-username';

@Component({
  selector: 'add-contact',
  template: `
   <form [formGroup]="customForm">
     <label>姓名:</label>
     <input type='text' formControlName='customName'/>
   </form>
  `
})

export class FormComponent{
  customForm = new FormGroup({
    customName: new FormControl('', validateUserName)
  });
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

分別定義了FormGroupFormControl的實例化對象

5 管道

Angular中,管道可以按照開發者指定的規則將模板內的數據進行轉換。

5.1 什么是管道

模板中,通過管道操作符 | 使用管道,| 左邊的為輸入數 據,右邊為管道。管道可以帶有參數,通過傳入參數輸出不同格式數據。同時,模板表達式中可以同時使用多個管道進行不同的處理。

<p>{{ birthday | date }}</p> <P>{{ birthday | data:"MM/dd/y" }}</p> <p>{{ expression | pipeName1 | pipeName2 }}</p>
  • 1
  • 2
  • 3
  • 4
  • 5

5.2 幾種管道

內置管道:Angular提供的,不需導入可以直接使用。

  • DatePipe:日期管道,格式化日期,純管道
  • JsonPipe:將輸入數據對象經過JSON.stringify()方法轉換后輸出對象字符串,非純管道
  • UpperCasePipe:文本中所有小寫字母全轉換為字母,純
  • LowerCasePipe:變成小寫,純
  • DecimalPipe:將數值按特定格式顯示文本,純
  • CurrencyPipe:數值轉化為本地貨幣格式,純
  • PercentPipe:數值轉百分比,純
  • SlicePipe:將數值或者字符串裁剪成新的子集,非純管道
expression | date: format

expression | json

expression | uppercase

expression | lowercase

expression | number[: digitInfo]

expression | currency[: currencyCode[: symbolDisplay[: digitInfo]]]

expression | percent

expression | slice: start[: end] 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

自定義管道:通過以下幾個步驟實現

  • 定義元數據:引入Pipe和PipeTransform,同時為管道命名
//sexreform.pipe.ts
import { Pipe, PipeTransform } from "@angular/core";

@Pipe {
  name: 'sexReform'
}

export class SexReform implements PipeTransform {
  //...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 實現transform方法
export class SexReform implements PipeTransform {
  transform(val: string): string {
    switch(val) {
      case 'male': return '男';
      case 'female' return '女';
      default: return '未知性別';
    }
  }
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 使用自定義管道
//使用管道前,需要在@NgModule的元數據declarations數組中添加自定義管道
import { SexReform } from 'pipes/sexreform.pipe';

@NgModule ({
  //...
  declarations: [SexReform]
})


//可以像內置管道一般使用自定義管道咯
@Component ({
  selector: 'pipe-demo-custom',
  template: `
    <p>{{ sexValue | sexReform }}</p>
  `
}) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

純管道與非純管道的區別:只有發生純變才會調用該管道,這類管道稱為純管道,其余的管道成為非純管道。純變指的是對基本數據類型(String、Number、Boolean)輸入值的變更或者對對象引用(Array、Function、Object)的更改。

只要數據發生改變,均會觸發非純管道,但不一定會觸發純管道,需要考察數據變化的情形是否為純變化。看下面這個例子:

//...
@Component ({
  selector: 'pure-pipe-demo',
  template: `
    <div>
      <p>{{ dateObj | date: "y-MM-dd HH:mm:ss EEEE" }}</p>
      <p>{{ dateStr | date: "y-MM-dd HH:mm:ss EEEE" }}</p>
    </div>
  `
})

export class PurePipeDemoComponent {
  dateObj: date = new Date('2016-06-08 20:05:08');
  dateStr: string = '2016-06-08 20:05:08';

  constructor(){
    setTimeout(() => {
    this.dateObj.setMonth(11),
    this.dateStr = '2016-12-08 20:05:08'
    },2000);
  }
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

初始日期分別為:

'2016-06-08 20:05:08 Wednesday'
'2016-06-08 20:05:08 Wednesday'
  • 1
  • 2

2s之后變成了

'2016-06-08 20:05:08 Wednesday'
'2016-12-08 20:05:08 Thursday'
  • 1
  • 2

你猜這是為什么呢??????


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM