第五章 数据绑定响应式编程和管道
Posted 跟我学angular
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第五章 数据绑定响应式编程和管道相关的知识,希望对你有一定的参考价值。
本章主要内容:
1.掌握单向绑定、双向绑定、类绑定和样式绑定的使用方法
2.掌握html基本属性绑定的方法
3.掌握Lambda表达式的使用方法
4.理解管道的概念
5.掌握管道的用法
6.理解响应式编程的概念
7.掌握响应式编程在Angular中的应用。
一、数据绑定
数据绑定允许将组件控制器的属性和方法与组件的模板连接起来,大大降低了开发时的编码量。例如:
(1)使用插值表达式将一个表达式的值显示在模板上:<h1>{{productTitle}}</h1>
(2)使用方括号将HTML标签的一个属性绑定到一个表达式上:<img [src] = “imgUrl”>
(3)使用小括号将组件控制器的一个方法绑定为模板上一个事件的处理器:
<button (click)=”toProductDetail()”>产品详情</button>
在angular中默认的绑定是单向的,即要么将组件控制器属性的变化反映到模板上(如(1)(2)),要么将模板上事件绑定到组件控制器的方法上(如(3))。angular 1中是双向绑定,但现在的angular版本中默认使用了单向绑定,而双向绑定作为用户开发时的可选项。
通常插值表达式与属性绑定都能获得同样的效果,例如<img [src] = “imgUrl”>语句也可以改为<img src = “{{imgUrl}}”>这种形式,在angular实际执行时,会将插值表达式翻译成属性绑定进行处理。
1.事件绑定
标准事件绑定语法如图5.1所示。
图5.1 标准事件绑定语法
上面这句是标准的事件绑定语法。为了给一个事件指定一个处理方法需要在一个组件模板中将事件的名字用小括号括起来。需要特别说明:
(1)图5.1中等号(=)右边的表达式可以不是一个函数调用,而是如下面所示的属性赋值:
<button (click) = “saved = true”>
即当button被点击时,saved被绑定为true。
下面用一个示例说明事件绑定的步骤,当点击页面上的“点我”按钮,就可以在控制台输出点击事件的内容。
在项目中创建一个bindComponent组件,修改bind.component.html模板页内容如下:
<button (click)="doOnClick($event)">点我</button>
在bind.component.ts文件中添加如下代码:
export class BindComponent implements OnInit {
constructor() {
}
ngOnInit() {
}
doOnClick() {
console.log(event);
}
}
(2)被绑定的事件即可以是标准的DOM事件,也可以是任意的自定义事件
2.DOM属性
在bind.component.html模板页添加如下代码:
1. <input value="Tom"(input)="doOnInput($event)">
在bind.component.ts文件中添加如下代码:
1. doOnInput(event:any){
2. console.log(event.target.value);//dom属性
3. console.log(event.target.getAttribute("value"));//html属性
4. }
从运行结果可以看出,DOM属性与html属性的关系:
少量HTML属性与DOM属性之间有着1:1的映射,如id;
有些HTML属性没有对应的DOM属性,如colspan;
有些DOM属性没有对应的HTML属性,如textContent;
就算名字相同,HTML属性和DOM属性也不是同一样东西;
HTML属性的值指定了初始值,DOM属性的值表示当前值;DOM属性的值可以改变,HTML属性的值不能改变;【即上述代码第二行的输出值会跟着输入的内容变化而变化,而第三行的输出值仍然是HTML属性指定的初始值】
模板绑定(插值表达式,如<h1>{{productTitle}}</h1>)是通过DOM属性和事件来工作的,而不是HTML属性。
3.HTML属性
(1)基本HTML属性绑定:
<td [attr.colspan]=”tableColspan”> 总计 <td>
例如下列代码在执行时会显示错误信息:Can't bind to 'colspan' since it isn't a known property of 'td'.因为{{1+1}}写法是DOM属性绑定,而'colspan'属于Html属性。
1. <table border="1">
2. <tr>
3. <tdcolspan="{{1+1}}">名称</td>
4. </tr>
5. </table>
所以基于上面的错误,只能使用下面的代码【即基本HTML属性绑定】来实现:
1. <table border="1">
2. <tr>
3. <td[attr.colspan]="size">名称</td>
4. </tr>
5. </table>
同时需要在bind.component.ts文件中加入size的变量定义,代码如下:
1. export class BindComponent implements OnInit {
2. size:number=2;
3. constructor() {
4. }
5. }
(2)CSS类绑定:
<div class =”aaa bbb” [class]=”表达式”>详细情况</div>
[class]后面的“表达式”值完全替换掉前面class的“aaa bbb”值。
<div [class.special]=”布尔值表达式”>详细情况</div>
当布尔值表达式为true时,div后面就会出现class,即使用class.special样式;当布尔表达式值为false时,div后就没有class,即不使用class.special样式。
<div [ngClass]=”{aaa:isA,bbb:isB}}”>
例如,在bind.component.css文件中添加如下代码:
1. .a{
2. background-color: yellow;
3. }
4. .b{
5. font-size: 80px;
6. }
7. .c{
8. color:red;
9. }
在bind.component.html文件中添加如下代码:
1. <div class="a b c">我的绑定属性</div>
运行后出现如图5.2所示效果图:
图5.2 通常CSS使用方法效果图
以上没有使用css类绑定,下面用css类绑定来解决这个问题:
在bind.component.html文件中添加如下代码:
<div [class]="divclass">我的绑定属性</div>
在bind.component.ts文件中加入如下代码:
export class BindComponent implements OnInit {
divclass:string;
constructor() {
setTimeout(()=>{
this.divclass="ab c"
},2000)
}
}
以上代码执行时刚开始显示如图5.3所示上方的效果,2秒后显示图5.3下方的效果。
图5.3 CSS类绑定效果图
有时候有些属性可能固定不变,而有些属性要根据条件发生变化,则可以使用如下步骤实现:
在bind.component.html文件中添加如下代码:
<div [class.c]="isRed">我的绑定属性</div>
在bind.component.ts文件中加入如下代码:
export class BindComponent implements OnInit {
isRed:boolean=false;
constructor() {
setTimeout(()=>{
this.isRed=true;
},2000)
}
}
以上代码运行结果是一开始字的颜色默认色,2秒后自动变为红色(当然是由样式CSS决定的红色)。但是这种方式只能控制一个CSS样式,如果要同时控制多个CSS样式,就需要使用下面的步骤:
在bind.component.html文件中添加如下代码:
<div [ngClass]="divClass" >我的绑定属性</div>
在bind.component.ts文件中加入如下代码:
export class BindComponent implements OnInit {
divClass:any={
a:false,
b:false,
c:false
};
constructor() {
setTimeout(()=>{
this.divClass ={
a:true,
b:true,
c:true
};
},2000)
}
}
上述代码的第2~6行表示开始时,控制a、b、c样式的值为false,即样式不生效;2秒后控制a、b、c样式的值为true,即样式生效。
(3)样式绑定:
1. <button [style.color]="isSpecial ? `red`: `green`">红</button>
此行代码表示,若isSpecial为true,则style的color为红色,否则为绿色。isSpecial在bind.component.ts文件中定义,代码如下:
1. export class BindComponent implements OnInit {
2. isSpecial:boolean=true;
3. constructor() {
4. setTimeout(()=>{
5. this.isSpecial =false;
6. },2000)
7. }
8. }
但这种方式也只能控制一个style,若要同时控制多个style,可以使用如下语句格式实现:
1. <div [ngStyle]= "{`font-style`:this.canSave?`italic`: `normal`}">
具体实现步骤如下:
在bind.component.html文件中添加如下代码:
<div [ngStyle]="divClass" >我的绑定属性</div>
在bind.component.ts文件中加入如下代码:
export class BindComponent implements OnInit {
divClass:any={
background:'green',
color:'red'
};
constructor() {
setTimeout(()=>{
this.divClass ={
background: 'red',
color: 'green'
};
},2000)
}
}
4.双向绑定
先看如下代码的实现过程:
在bind.component.html文件中添加如下代码:
<input [value]="name"(input)="doOnInput($event)">
{{name}}
在bind.component.ts文件中加入如下代码:
export class BindComponent implements OnInit {
name: string;
constructor() {
setInterval(()=>{
this.name="Angular"
},3000)
}
ngOnInit() {
}
doOnInput(event) {
this.name=event.target.value
}
}
从以上代码可以看出,当用户在文本框中输入新的内容时,文本框后面显示的内容也会跟着改变;但每隔3秒会将文本框中内容还原成“Angular”值。以此可以看出要实现双向绑定,这种方法即要绑定属性又要绑定事件,而在Angular中,提供了如下方法实现:
在app.module.ts文件中添加第1行和第11行代码,用以引入FormsModule后,才可以使用ngModel。
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { BindComponent } from './bind/bind.component';
@NgModule({
declarations: [
AppComponent,
BindComponent
],
imports: [
BrowserModule,
FormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
在bind.component.html文件中添加如下代码,
<input [(ngModel)]="name">
{{name}}
双向绑定可以使视图和模型的数据保持同步,无论是视图或模型的哪一方发生改变,另一方都会立即同步。
二、管道
管道负责原始值到显示值的转换,也就是可以规定输出值的格式。比如:
在bind.component.html文件中添加如下代码,
<p>
今天的日期是:{{today}}
</p>
在bind.component.ts文件中加入如下代码:
export class BindComponent implements OnInit {
today:Date=new Date();
constructor() {
}
ngOnInit() {
}
}
运行结果如图5.4上方所示。若使用如下管道代码后,其运行结果如图5.4下方所示。
1. <p>
2. 今天的日期是:{{today |date:'yyyy-MM-dd hh:mm:ss'}}
3. </p>
图5.4 管道运行效果
再比如number(数字)管道:若要控制6345.234的输出格式为6位整数位,2位小数位,则可以使用如下代码:
1. <p>
2. 你的工资待遇是:{{salary |number:'6.2-2'}}
3. </p>
“6.2-2”中6表示6位整数,前面的2表示至少几位小数,后面的2表示最多几位小数。
再比如async(异步)管道用于在页面上处理业务的流。这些都是Angualr封装了的常用的内置管道如下表所示,内置管道可以直接在任何模版表达式中使用,不需要通过import导入和在模块中声明。
表内置管道
管道 |
类型 |
功能 |
Date |
纯管道 |
日期管道,格式化日期 |
Json |
非纯管道 |
将输入数据对象经过JSON.stringify()方法转换后输出对象的字符串 |
UpperCase |
纯管道 |
将文本所有小写字母转换成大写字母 |
LowerCase |
纯管道 |
将文本所有大写字母转换成小写字母 |
Decimal |
纯管道 |
将数值按特定的格式显示文本 |
Currentcy |
纯管道 |
将数值转百分比格式 |
Slice |
非纯管道 |
将数组或者字符串裁剪成新子集 |
其实实际应用中,还需要根据要求自定义输出格式,即自定义管道。自定义管道步骤如下:
(1)在项目中新建一个pipe目录;
(2)在pipe目录名上右击,选择菜单中的“New…Angular CLI…”,然后在弹出的快捷菜单中选择pipe项,输入管道的名字即可生成自定义管道文件;
(3)此时在pipe目录下生成两个文件:mypipe.pipe.ts和mypipe.pipe.spec.ts,mypipe.pipe.ts文件核心代码如下:
1. import { Pipe, PipeTransform } from '@angular/core';
2. @Pipe({
3. name: 'mypipe'
4. })
5. export class MypipePipe implements PipeTransform {
6. transform(value: any, args?:any): any {
7. return null;
8. }
9. }
其中第3行name后的mypipe是使用的管道的名字,即后面要使用这个管道时就用这个名字;第6行的方法transform接收一个输入值value(管道输入的原始值),args是一个可选参数,transform根据原始值和可选参数来转换,最后通过return把转换后的值返回回去。例如下面的代码:
1. export class MypipePipe implements PipeTransform {
2. transform(value: number, args?:number): any {
3. if(!args){
4. args=1;
5. }
6. return value*args;
7. }
8. }
上面代码表示,当args不存在时,args=1,并最终返回处理后的结果(value*age)。使用格式如下(在bind.component.html文件中加入如下代码,age在bind.component.ts中定义并初始化为3,此时页面输出“自定义管道12”):
1. <p>
2. 自定义管道{{age | mypipe:'4'}}
3. </p>
三、用管道改造第四章的新闻发布系统——实现新闻标题过滤功能
1.改造效果介绍
当在文本框中输入过滤关键字时,在新闻显示列表中显示过滤结果,当文本框中没有输入任何内容,在新闻显示列表中显示所有内容。具体效果图见图5.5。
图5.5 新闻过滤效果
2.改造步骤
(1)在页面上添加1个输入框,即在news.component.html文件中添加如下代码:
1. <div>
2. <divclass="col-sm-12">
3. <divclass="form-group">
4. <inputclass="form-control" placeholder="请输入要搜索的新闻标题">
5. </div>
6. </div>
7. </div>
(2)为了使用响应式编程,需要在app.module.ts文件中的导ReactiveFormsModule包,具体位置如下:
1. imports: [
2. BrowserModule,
3. RouterModule.forRoot(routeConfig),
4. ReactiveFormsModule
5. ],
(3)在product.component.ts文件中实现输入模块处理代码:
1. export class NewsComponent implements OnInit {
2. news: Array<News>;
3. findTitle: string;
4. titleControl: FormControl = newFormControl();
5.
6. constructor(privatenewsService: NewsService) {
7. this.titleControl.valueChanges
8. .debounceTime(500)
9. .subscribe(value =>this.findTitle = value); // 使用此语句需要导入rxjs/Rx包
10. }
11. insertNews() {
12. this.news.push(new News(7, '第7条新闻','2017-1-27', 4.5, '这是第7个新闻的详细信息',['普通新闻']));
13. } ngOnInit() {
14. this.news =this.newsService.getAllNews();
15.
16. }
17. }
(4)用formControl指令将控制器中的titleControl与输入框进行绑定,代码就是将(1)中的第4行代码改为如下:
1. <input placeholder="请输入产品名称"[formControl]="titleControl">
(5)编写管道实现过滤器功能,即挑选出与输入框输入内容标题匹配的新闻,并返回
首先在项目的app文件夹下创建pipe文件夹,然后使用菜单中的“New…Angular CLI…”命令生成自定义管道文件。(本例中newspipe.pipe.ts),在newspipe.pipe.ts文件中与入如下代码:
1. import {Pipe, PipeTransform} from '@angular/core';
2.
3. @Pipe({
4. name: 'newspipe'
5. })
6. export class NewspipePipe implements PipeTransform {
7.
8. transform(allNews: any,findTitle: any): any {
9. if (!findTitle) {
10. return allNews;
11. }
12. return allNews.filter(item=> item.title.includes(findTitle));
13. }
14.
15. }
以上代码第8行中allNews表示要在哪儿搜索, 表示根据标题findTitle要过滤的具体内容。
(6)将管道应用到模板news.component.html中,代码如下:
1. <div *ngFor="let news of news|newspipe:findTitle"class="col-md-4 col-sm-4 col-lg-4">
以上是关于第五章 数据绑定响应式编程和管道的主要内容,如果未能解决你的问题,请参考以下文章
Swift系列三十四 - 响应式编程(RxSwift的使用)