Angular 模板:数据\事件绑定、内置命令

一、数据绑定

1、根据数据流方向可以分为三种:

单向:从数据源到视图:

//插值DOM元素属性
<p>{{ detail.telNo}}</P>

//绑定HTML标签特性
<div [title]="sex"></div>

//绑定--属性绑定
<div [style.color]="color">hello world</div>

  单向:从视图到数据源:

//事件绑定
(click)="showDetail()"
on-click="showDetail()"

  双项:

//双向绑定
<div [(title)]="subject"></div>
<div bindon-title="subject"></div>

数据绑定是借助于DOM对象属性和事件来运作的。

2、属性绑定

DOM元素属性绑定:把DOM对象属性绑定到组件的属性上,而绑定目标可以是中括号,也可以加前缀,还可以使用属性绑定设置自定义组件的输入属性。

//中括号 --HTML标签div的属性title
<div [title]="titleText"></div>

//加前缀
<div bind-title="titleText"></div>

//自定义组件的输入属性
<user-detail [user]="curUser"></user-detail> 

//table 的属性colspan, 注意attr
<table>
  <tr>
    <td [attr.colspan]="{{ 1 + 2 }}"></td>
  </tr>
</table>

3、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 ? 'green' : 'red'">若canClick为true,则按钮背景颜色为green</button>

<button [style.font-size.px]="isLarge ? 19 : 3">若isLarge为true,则按钮字体变为18px</button>

二、自定义事件的绑定

借助EventEmitter实现。它的实现步骤:一、在组件中创建EventEmitter实例对象,并将其以输出属性形式暴露;二、父组件通过绑定一个属性来自定义一个事件;三、在组件中调用EventEmitter.emit()触发自定义事件;四、父组件绑定的事件通过$event对象访问数据。

//父组件collection.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'collection',
  template: `<contact-collect [contact]="detail" (onCollect)="collectMoney($event)"></contact-collect>`
})

export class CollectionComponent implements OnInit{
  detail: any = {};
  collectMoney(){
    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)="collectMoney()">收藏</i>`
})

export class CollectionComponent implements OnInit{
  @Input() contact: any = {};
  @Output() onCollect = new EventEmitter<boolean>();
  collectMoney(){
    this.onCollect.emit();
  }
}

子组件click事件触发collectMoney()方法,该方法调用EventEmitter实例化对象onCollect()emit方法,向父组件发送数据;父组件绑定了子组件的onCollect事件,该事件被触发后将调用父元素的collectMoney($event)方法,并以$event访问数据。

三、内置命令

1、ngClass:通过它,可以动态添加或移除多个类。NgClass绑定一个由CSS类名:value的对象,value是一个布尔类型的数据值,当valuetrue时添加对应的类名到模板元素中,反之删除。

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

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

2、NgStyle:设置多个内联样式。绑定刑如CSS属性名:value的对象。

setStyles(){
  let styles = {
    'color': this.red ? 'red' : 'blue',
    'font-size': !this.font13 ? '13px' : '19px',
    'font-weight': this.isSpecial ? 'bold' : 'normal' 
  };
  return styles;
}

<div [ngStyle]="setStyles"></div> 

3、NgIf:绑定一个布尔类型的表达式,当表达式真时候,DOM树节点上添加一个元素及其子元素,否则移除(查看DOM树不能看到该元素)。

<ion-col size="6">
     <span class="gray_font_color">数量:</span>
     <span *ngIf="cargo.quantity">{{cargo.quantity}}{{cargo.quantityUnit}}</span>
     <span *ngIf="!cargo.quantity">无</span>
 </ion-col>

4、NgSwitch:根据NgSwitch绑定的模板表达式返回值决定添加那个模板元素到DOM节点上。

     <ion-col size="1" [ngSwitch]="adrsType">
        <div *ngSwitchCase="addressTypeEnmus.PICKUP_POINT" class="pick-unld-point bg-green">装</div>
        <div *ngSwitchCase="addressTypeEnmus.UNLOADING_POINT" class="pick-unld-point bg-red">卸</div>
      </ion-col>

5、NgFor:重复执行某些步骤来展现数据,它支持一个可选的index索引,下标范围为0<=index<数组的长度。

      <div *ngFor="let pickAdrs of commPickAdrslist;let i=index" >
            <app-order-address [adrs] = "pickAdrs" [adrsIndex]="i" [adrsType]="addressTypeEnmus.PICKUP_POINT"
                               (onDelAdrs)="delAdrs($event)" (onGainAdrs)="gainAdrs($event)" (onAddAdrs)="addAdrs($event)"
                               (onEditAdrs)="editAdrs($event)"></app-order-address>
        </div>

6、NgForTrackBy:每次更改都会引发很多相关联的DOM操作,使用NgFor会让性能变得很差,比如重新从服务器拉取列表数据,虽然大部分数据没变化,但是因为不知道哪些数据变化了,需要清空并重新渲染。可以通过使用追踪函数避免重复渲染的性能浪费。【待深入研究】

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

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

四、管道

1 、什么是管道

Angular中,管道可以按照开发者指定的规则将模板内的数据进行转换。

模板中,通过管道操作符 | 使用管道,| 左边的为输入数 据,右边为管道。管道可以带有参数,通过传入参数输出不同格式数据。同时,模板表达式中可以同时使用多个管道进行不同的处理。

2 、几种管道

1)内置管道:Angular提供的,不需导入可以直接使用。

  • DatePipe:日期管道,格式化日期,纯管道
  • JsonPipe:将输入数据对象经过JSON.stringify()方法转换后输出对象字符串,非纯管道
  • UpperCasePipe:文本中所有小写字母全转换为字母,纯
  • LowerCasePipe:变成小写,纯
  • DecimalPipe:将数值按特定格式显示文本,纯
  • CurrencyPipe:数值转化为本地货币格式,纯
  • PercentPipe:数值转百分比,纯
  • SlicePipe:将数值或者字符串裁剪成新的子集,非纯管道
expression | date: format

expression | json

expression | uppercase

expression | lowercase

expression | number[: digitInfo] 例如:保留三位小数
<span *ngIf="cargo.weight">{{cargo.weight |  number : '1.0-3'}}{{weightUnit[cargo.weightUnit]}}</span>

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

expression | percent

expression | slice: start[: end]

2)自定义管道

  • 定义元数据:引入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('2020-06-09 19:19:09');
  dateStr: string = '2020-06-09 19:19:09';

  constructor(){
    setTimeout(() => {
    this.dateObj.setMonth(11),
    this.dateStr = '2020-12-03 03:03:03'
    },3000);
  }
} 

初始日期分别为:

2020-06-09 19:19:09  Friday
2020-06-09 19:19:09  Friday

3S后变成:

2020-06-09 19:19:09  Friday
2020-12-03 03:03:03  Thursday