Angular 2-模板

1 什麼是模板

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

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>

2 數據綁定

根據數據流向能夠分爲三種:html

  • 單向:數據源到視圖
//插值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>

NOTE git

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

數據綁定是藉助於DOM對象屬性和事件來運做的。json

2.1 插值

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

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>

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

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

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>

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

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

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

特有的綁定方式:[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>

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>

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>

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

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

自定義事件:藉助EventEmitter實現。它的實現步驟:1、在組件中建立EventEmitter實例對象,並將其以輸出屬性形式暴露;2、父組件經過綁定一個屬性來自定義一個事件;3、在組件中調用EventEmitter.emit()觸發自定義事件;4、父組件綁定的事件經過$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();
  }
}

子組件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"/>

`[ ]`實現了數據流從組件類到模板,`( )`實現了數據流從模板到組件類。
###2.6 輸入輸出屬性###
綁定聲明中,「=」左側的稱爲綁定目標,「=」右側稱爲綁定源。
    
    //list.component.html
<list-item [contact]="contact" (routerNavigate)="routerNavigate($event)"></list-item>

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

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

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

//元數據方式
@Component({
  outputs: ['clicks:goto']
})

3 內置指令

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

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


<div [ngClass]="setClass()"></div>

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>

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

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

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

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

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

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

NgForTrackBy:每次更改都會引起不少相關聯的DOM操做,使用NgFor會讓性能變得不好,好比從新從服務器拉取列表數據,雖然大部分數據沒變化,可是由於不知道哪些數據變化了,須要清空並從新渲染。能夠經過使用追蹤函數避免重複渲染的性能浪費。

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

<div *ngFor="let contact of contacts; trackBy: trackByContacts">{{ contact.id }}</div>

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 {}

###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
  ]
})

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

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

控件中使用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" />男

複選框:NgModel會綁定一個布爾值

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

單選下拉框:option綁定目標有兩種,value和ngValue,value返回值類型爲基本數據類型,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>

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

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

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

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

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

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

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

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

局部變量contactName是NgModel指令實例對象的引用,能夠經過它讀取NgModel的屬性值。

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

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

表單狀態檢驗有三個時段,初始狀態、輸入後狀態(valid、pristine、dirty狀態改變)、失去焦點後狀態(touched和untouched狀態改變)。

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>

//這是form中的數據格式
{
  nameGroup: {
    firstName: '',
    lastName: ''
  }
}

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){

  }
}

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>

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: '用戶名必須是手機號或者郵箱帳號'
    }
  };
}
  • 使用自定義檢驗
//...
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 {}

首先要導入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)
  });
}

分別定義了FormGroup和FormControl的實例化對象

5 管道

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

5.1 什麼是管道

模板中,經過管道操做符 | 使用管道,| 左邊的爲輸入數 據,右邊爲管道。管道能夠帶有參數,經過傳入參數輸出不一樣格式數據。同時,模板表達式中能夠同時使用多個管道進行不一樣的處理。

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

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

<p>{{ expression | pipeName1 | pipeName2 }}</p>

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]

自定義管道:經過如下幾個步驟實現

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

@Pipe {
  name: 'sexReform'
}

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

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


//能夠像內置管道通常使用自定義管道咯
@Component ({
  selector: 'pipe-demo-custom',
  template: `
    <p>{{ sexValue | sexReform }}</p>
  `
})

純管道與非純管道的區別:只有發生純變纔會調用該管道,這類管道稱爲純管道,其他的管道成爲非純管道。純變指的是對基本數據類型(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);
  }
}

初始日期分別爲:

'2016-06-08 20:05:08 Wednesday'
'2016-06-08 20:05:08 Wednesday'

2s以後變成了

'2016-06-08 20:05:08 Wednesday'
'2016-12-08 20:05:08 Thursday'
相關文章
相關標籤/搜索