它是页面呈现的骨架,是盛装组件数据的容器。与之相关的内容包括了模板与组件件数据交互、内置指令、表单、管道等。css
//插值:绑定属性变量的值到模板中 <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>
根据数据流向能够分为三种: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
Property
为DOM
对象属性,以DOM
元素做为多想,其附加内容,是在文档对象模型里定义的,如childNodes、firstChild
。 而Attribute
为HTML标签特性,是DOM
节点自带的属性,在HTML中定义的,即只要是在HTML标签中出现的属性(HTML代码)都是Attribute。express
数据绑定是借助于DOM对象属性和事件来运做的。json
双大括号{{ }}语法来实现。bootstrap
相似于JS的表达式,绝大多数JS表达式均为合法模板表达式。它应用于插值语法双大括号中和属性绑定“=”右侧的括号中。但如下JS表达式不是合法模板表达式:数组
模板表达式不支持位运算。浏览器
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>
单向,数据从模板到组件类流动。Angular监听用户操做时间,如键盘事件、鼠标事件、触屏事件等方法。事件绑定的语法为:“=”左侧小括号内的目标事件和“=”右侧引号中的模板语句组成。
模板语句与模板表达式同样,和JS表达式相似,有一些JS表达式在模板语句中不被支持:
模板语句和模板运算符同样,只能访问其上下文环境的成员,模板语句的上下文环境就是绑定事件对应组件的实例。也能够包含组件外的对象,如模板局部变量和事件绑定语句中的$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'] })
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>
HTML内置表单标签一些特性存在浏览器兼容性问题,在自定义规则、表单数据获取、处理、提交等流程都比较复杂。Angualr提供了双向数据绑定、强大的检验规则以及自定义检验错误提示等功能。Angular提供了模板驱动(使用模板表单内置指令、内置检验方式)和模型驱动(自定义)两种方式构建表单。
@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的使用步骤以下:
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"/>
表单指令局部变量:表单指令的局部变量在定义时需手动初始化为特定指令的表明值,最终解析后会被赋值为表单指令实例对象的引用。
<form #contactForm="ngForm"> //... </form>
局部变量#contactForm为NgForm指令实例对象的引用,能够在模板中读取NgForm实例对象的属性值,如追踪表单的valid属性状态。
<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状态改变)。
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){ } }
.ng-valid[required] { border-left: 5px solid #0f0; } .ng-invalid { border-left: 5px solid #f00; } <p [hidden]="contactName.valid || contactName.pristine">用户名长度为3-10个字符</p>
表单内置检验:required
、minlength
、maxlength
、pattern
表单自定义检验
//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的实例化对象
Angular中,管道能够按照开发者指定的规则将模板内的数据进行转换。
模板中,经过管道操做符 | 使用管道,| 左边的为输入数 据,右边为管道。管道能够带有参数,经过传入参数输出不一样格式数据。同时,模板表达式中能够同时使用多个管道进行不一样的处理。
<p>{{ birthday | date }}</p> <P>{{ birthday | data:"MM/dd/y" }}</p> <p>{{ expression | pipeName1 | pipeName2 }}</p>
内置管道:Angular提供的,不需导入能够直接使用。
expression | date: format expression | json expression | uppercase expression | lowercase expression | number[: digitInfo] expression | currency[: currencyCode[: symbolDisplay[: digitInfo]]] expression | percent expression | slice: start[: end]
自定义管道:经过如下几个步骤实现
//sexreform.pipe.ts import { Pipe, PipeTransform } from "@angular/core"; @Pipe { name: 'sexReform' } export class SexReform implements PipeTransform { //... }
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'