5.揭秘angular2学习 ------- 管道

管道简介

管道是什么?

Angular的管道可以看作成是一个数据格式化展示的工具。管道可以将数据格式化显示,而不改变源数据。比如关于日期的展示,对于源数据使用管道1可以以yyyy/MM/dd来展示,也可以使用管道2展示成Feb 28, 2017的形式。但原数据依然是date,并没有改变。利用管道我们可以将数据格式化的内容剥离出来,使之独立,有需要格式化展示的时候选择相应的管道进行处理即可。

如何使用

在Angular中需要使用管道操作符“|”来使用管道。形式如:

{{ 输入数据 | 管道 : 管道参数 }}
template: `
    <p>My birthday is {{ birthday | date: "MM/dd/y" }}
`
...
export class PipeDemoComponent {
    birthday = new Date(1993, 3, 15);
}

链式管道

管道可以像jQuery一样链式使用。

{{ data | Pipe1 | Pipe2 | Pipe3 | ... }}

表达式从左至右求值,date先通过Pipe1处理后将处理后的数据作为输入数据再进行Pipe2的处理,以此类推,直到所有管道都已执行完毕。

这种链式调用的方式可以展示更丰富更复杂的数据。

Angular内置管道

先说个题外话,学习到这里也总结出了一点Angular的规律,不知道各位看官发现没有:Angular2框架虽然看起来比较庞大,但是其支持的内容全是全面。比如我们现在学的管道与之前学的表单,以及将来要学习的指令等,Angular都会内置一些常用的,然后还支持自定义扩展,我很喜欢这种方式。题外话完毕。

Angualr封装了一些常用的内置管道。内置管道可以直接在任何模版表达式中使用,不需要通过import导入和在模块中声明。

Angular内置管道整理如图:

管道类型功能
DatePipe纯管道日期管道,格式化日期
JsonPipe非纯管道将输入数据对象经过JSON.stringify()方法转换后输出对象的字符串
UpperCasePipe纯管道将文本所有小写字母转换成大写字母
LowerCasePipe纯管道将文本所有大写字母转换成小写字母
DecimalPipe纯管道将数值按特定的格式显示文本
CurrentcyPipe纯管道将数值转百分比格式
SlicePipe非纯管道将数组或者字符串裁剪成新子集

下面会分别讲解每一个管道的用法。

DatePipe

关键词:日期
DatePipe用来格式化日期数据,使用方法如下:

expression | date: format
  1. expression可以是
    • Date日期对象
    • 日期字符串,如”2016/04/05”
    • 毫秒级时间戳
  2. format自定义日期格式如下表(取2016-06-08 20:05:08时间为例)
日期标志符缩写全称单标志符双标志符
地区GG(AD)GGGG(Anno Domini)--
y--y(2016)yy(16)
MMMM(Jun)MMMM(June)M(6)MM(06)
d--d(8)dd(08)
星期EEEE(Fri)EEEE(Friday)--
时间(AM,PM)j--j(8 PM)jj(08 PM)
12小时制时间h--h(8)hh(08)
24小时制时间H--H(20)HH(20)
m--m(5)mm(05)
s--s(8)ss(08)
时区Z-Z(china Standard Time)--
时区zz(GMT-8:00)---

下面看例子:

@Component({
    selector: 'pipe-demo-date',
    template: `
        <p>{{date|date: "y-MM-dd EEEE"}}</p>
    `
})
export class PipeDemoDateComponent {
    date:Date = new Date('2016-06-08 20:05:08');
}

输出结果为

2016-06-08 Wednesday

JsonPipe

关键词:Json
JsonPipe管道通过JSON.stringify()来将输入数据对象转换成对象字符串,该管道主要用于开发调试:

@Component({
    selector: 'pipe-demo-json',
    template: `
        <pre>{{jsonObject | json}}</pre>
    `
})
export class PipeDemJsonComponent {
    jsonObject: Object = {foo: 'bar', baz: 'qux', nested: {xyz: 3, numbers: [1, 2]}};
}

输出结果为:

//json
{
    "foo": "bar",
    "baz": "qux",
    "nested": {
        "xyz": 3,
        "numbers": [
            1,
            2
        ]
    }
}

UpperCasePipe

UpperCasePipe管道用于将文本中所有小写字母转换成大写字母。
语法格式:

expression | uppercase

LowerCasePipe

LowerCasePipe管道用于将文本中所有大写字母转换成小写字母。
语法格式:

expression | lowercase

DecimalPipe

关键词:数值位数
DecimalPipe管道用于对数值的整数与小数部分按照指定规则进行格式化,这种格式化方式也成为本地格式化处理,语法如下:

expression | number[:digitInfo]

参数digitInfo的格式如下:

{minIntegerDigits}.{minFractionDigits}-{maxfractionDigits}
  • minIntegerDigits:整数部分保留最小的位数,默认值为1.
  • minFractionDigits:小数部分保留最小的位数,默认值为0.
  • maxFractionDigits:小数部分保留最大的位数,默认值为3.

用法如下:

@Component({
    selector: 'pipe-demo-number',
    template: `
        <div>
            <p>A 变量:{{a | number: '3.4-5'}}</p>
            <p>B 变量:{{b | number: '3.1-5'}}</p>
        </div>
    `
})
export class PipeDemoNumberComponent {
    a: number = 2.718281828459045;
    b: number = 33456;
}

转换后结果:

a 变量:002.71828
b 变量:33,456.0

格式化变量a所采用的参数为3.4-5,参数.左边的3表示整数位最少保留三位,原值整数位为1位不足3位,所以用0补齐。参数.右边的4-5表示保留小数的最小数位为4为,最大数位为5位,因原始数据小数位大于5位,所以保留四舍五入后的5位小数。

格式化变量b所采用的参数为3.1-5,参数.左边的3表示整数位最少保留三维,原值正式位为5位,大于3位,所以全部保留。参数.右边的1-5表示保留小数的最小数位为1位,最大数位为5位,因原始数据没有小数位,因此采用最小1位限制的规则,小数位补0。

CurrencyPipe

关键词:货币
CurrentPipe管道可以将数值进行货币格式化处理。语法如下:

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

- 参数currentcyCode:表示要格式化的目标货币格式,值为ISO 4217货币码,如CNY人民币、USD美元、EUR欧元等。
- 参数symbolDisplay:表示以该类型货币的哪种格式显示,值为布尔值,true表示显示货币符号如¥、$等,false表示显示ISO 4217货币码如CNY、USD等。
- digitInfo:参考DecimalPipe管道。

来个例子:

@Component({
    selector: 'pipe-demo-currency',
    template: `
        <p>A 变量:{{ a | currency: 'USD': false }}</p>
        <p>B 变量:{{ b | currency: 'CNY': true: '3.1-3'}}</p>
    `
})
export class PipeDemoCurrencyComponent {
    a: number = 0.259;
    b: number = 20.6745;
}

转换后结果

A 变量:USD0.259
B 变量:¥020.675

PercentPipe

关键词:百分数
PercentPipe管道可以对数值进行百分比处理。语法如下:

expression | percent[: digitInfo]
  • 参数digitInfo:参考DecimalPipe管道

直接看例子:

@Component({
    selector: 'pipe-demo-Percent',
    template: `
        <div>
            <p>A 变量:{{ a | percent }}</p>
            <p>B 变量:{{ b | percent: '3.1-3' }}</p>
        </div>
    `
})
export class PipeDemoPercentComponent {
    a: number = 0.259;
    b: number = 0.657;
}

输出:

A 变量:25.9%
B 变量:065.7%

注意

这里的百分化处理是在原值的基础上进行乘以100%,而不是简单的字符串拼接。

SlicePipe

关键词:截断
SlicePipe管道用于裁剪数组字符串,返回裁剪后的目标子集。语法如下:

expression | slice: start[: end]

SlicePipe是基于Array.prototype.slice()方法和String.prototype.slice()方法来实现的。

自定义管道

老规矩,Angular给了一些内置管道,那么肯定还可以自定义管道来解决特定的问题。下面就来学习如何自定义管道。

需求:将male展示为男,female展示为女

1.创建自定义管道

import { Pipe, PipeTransform } form '@angular/core';  //引入PipeTransform是为了继承transform方法

@Pipe({ name: 'sexReform' })  //name属性值惯用小驼峰是写法
export class SexReformPipe implements PipeTransform {
    transform(value: string): string {
        switch(value){
            case 'male': return '男';
            case 'female': return '女';
            default: return '不男不女或雌雄同体';
        } 
    }
}

2.使用自定义管道

//...
<p> {{ sexValue | sexReform }}</p>
<p class="{{ sexValue | sexReform }}"</P>   //将返回值赋值给class
//...

3.注意:管道返回值不能返回html文档,会被转移展示!!不要用这种方法来创建节点。

管道的变化监测

在之前的数据绑定中,学习到Angular的变化监测机制,如果频繁的触发变化监测会引起性能问题。
但我们可以通过使用管道,让Angular选择使用更简单、更快速的变化监测策略来提高性能。
下面使用管道实现一个过滤联系人列表功能的例子来了解Angular管道的性能优化:

//...
@Pipe({ name: selectContanct })
export class SelectContanctPipe implements PipeTransform{
    transform(allContacts: Array, prefix: string){
        return allContacts.filter(contact => contact.name.match("^"+prefix));
    }
}
@Component({
    selector: 'pipe-demo',
    template: `
        <input type="text" #box (keyup.enter)="addContact(box.value); box.value=''" placeholder="输入联系人后回车添加" />
        <div *ngFor="let contact of (contacts | selectContactPipe: '李')">
           {{contact.name}}
        </div>
    `
})
export class PipeDemoComponent {
    contacts = [{name: '张三'}, {name: '李四'}];
    addContact(name: string) {
        this.contacts.push({name});
    }
}

上面的管道过滤只显示”李”姓的联系人,如果用户输入一个”李”姓联系人,然后回车将该联系人添加至contacts数组中。然后我们会觉得联系人列表应该会实时显示新的联系人,然而并没有
这是因为Angular优化了管道的监测机制,它会忽略对象内值的变化,只会监测指向对象的指针是否发生改变。

这种管道称为纯管道,虽然纯管道优化了性能,但有时无法满足需求,就像上面的例子那样。
这时我们就需要另外一种变化监测机制,也就是非纯管道

Angular管道有两种变化监测机制,一种是纯管道(默认),另一种即非纯管道。

纯管道 (Pure Pipe)

  1. 使用纯管道时,只有监测到输入值发生纯变更才会调用纯管道的transform方法来转换数据。
  2. 纯变更是指对基本数据类型输入值的变更,或对对象的引用放生改变。

非纯管道 (Impure Pipe)

使用非纯管道,Angular组件在每个变化监测周期都会调用非纯管道。
1.设置非纯管道

@Pipe({ 
    name: 'selectContact',
    pure: false    //设置非纯管道
})

扩展阅读

安全导航操作符

Angular的模版表达式在某些特定场景中允许使用一些特殊的连接操作符,如本章介绍的管道操作符。
下面来学习安全导航操作符”?.”。
假设模版表达式detail.telNum的值为0123456789,下面的代码正常运行后界面将会显示为0123456789,代码如下:

<p>{{ detail.telNum }}</p>

如果模版变量detail没被赋值,在Angular会报错,程序无法运行。
我们可以使用以下处理方式规避这种错误:

<p>{{ detail && detail.telNum }}</p>  //疑问,这里的结果会是什么?

上述方法可以达到预期,但是遇到长属性路径时会显得臃肿。这时我们可以使用”?.”安全导航操作符来处理,避免因为属性路径中出现null或者undefined值而出现的错误。

<p>{{ detail?.telNum }}</p>

双向绑定的原理

在模版部分的学习中,我们已经了解到,[(ngModel)]可以拆分为ngModel和ngModelChange两部分。其中,ngModel指令的输入属性用来设置元素的值,ngModelChange作为NgModel指令的输出属性用来监听元素值是否变化。
需要注意的是,ngModelChange属性并不会生成DOM事件,实际上它是一个EventEmitter类型的对象,[(ngModel)]的具体实现如下:

@Direvtive({
    selector: '[ngModel]',
    host: {
            "[value]": "ngModel",
            "(input)": "ngModelChange.next($event.target.value)"
    }
})
class NgModelDirective {
    @Input() ngModel: any;
    @Output() ngModelChange: EventEmitter = new EventEmitter();
}

host属性用来描述和指令元素相关的输入输出属性变化,即当[ngModel]的ngModelChange事件发生时就会触发input事件,当[ngModel]的ngModel值变化时就会更新value属性。

Angular提供了一种双向数据绑定的语法,即[(x)]。也就是说当Angular解析一个[(x)]的绑定目标时,相当于为这个x指令绑定一个名为x的输入属性和一个名为xChange的输出属性,示例代码如下:

<span [(x)]="e"></span>
//等同于下面的代码
<span [x]="e" (xChange)="e=$event"></span>

总的来说,双向数据绑定实际上就是通过输入属性存储数,同时通过一个与之对应的输出属性(输入属性+Change后缀)监听输入属性的数据变化来触发相应的事件。

了解Angular双向数据绑定的原理后,接下来通过创建一个支持双向绑定的组件例子来加深理解。该例子绑定一个Number的输入输出属性,同时在组件需要定义一个@Output输出属性来匹配@Input输入属性,示例代码如下:

//amount.component.ts
import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
    selector: 'amount',
    template: `
        <span>
            子组件当前值: {{value}} -
            <button (click)="increment()"> 增加 </button>
        </span>
    `
})
export class AmountComponent {
    @Input() value: number = 0;
    @Output() valueChange: EventEmitter<number> = new EventEmitter<number>();

    increment(){
        this.value++;
        this.valueChange.emit(this.value);
    }
}
//app.component.ts
import { Component, Input } from '@angular/core';
import { AmountComponent } from './amount.component';

@Component({
    selector: 'app',
    template: `
        <div>
            <div>
                <span>Number 1:</span>
                <amount [(value)]="number1"></amount>

            </div>
            <div>
                <span>Number 2:</span>
                <amount [(value)]="number2" (valueChange)="number2=$event"></amount>
            </div>
            <ul>
                <li>Number 1:父组件当前值:{{number1}}</li>
                <li>Number 2:父组件当前值:{{number2}}</li>
            </ul>
        </div>
    `
})
export class Parent {
    number1: number = 0;
    number2: number = 2;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值