• 三: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

    你猜这是为什么呢??????

  • 相关阅读:
    less @import and extend及mixin详解
    Less的guards and argument matching
    LESS嵌套中的Mixins和classes
    bootstrap colorscheme以及theme自动生成
    C# Winform 获得下拉框 选中的值
    C# 后台按键 视频播放器 全屏后无法 触发
    C# 调用win32API 获取进程句柄 有毛用???
    C# 键盘钩子
    C# SqlParameter 使用
    C# 获得星期几
  • 原文地址:https://www.cnblogs.com/zzy-run-92/p/9396756.html
Copyright © 2020-2023  润新知