angular2零碎剖析

1. *ngFor

*ngFor指令定义了一些行属性:

export declare class NgForRow {
    $implicit: any;
    index: number;
    count: number;
    constructor($implicit: any, index: number, count: number);
    first: boolean;
    last: boolean;
    even: boolean;
    odd: boolean;
}

它们只能用在*ngFor语句中,如果在语句外部使用我们需要导出为局部变量。

<a *ngFor="let hero of heroes; let index = index">
    {{index + ". " + hero.name}}
</a>

$implicit为当前行本身,实际上就是我们这里的单个hero。其它几个属性都见名知意。

*ngFor的语句实际上属于Angular特有的微语法,它被Angular自身解析。

*ngFor正因为常常操作大量数据而应当在性能上被我们格外关注,首先*ngFor的改变规则

  1. 添加项目时,模板的新实例将添加到DOM。
  2. 当项目被删除时,其模板实例将从DOM中删除。
  3. 当项目重新排序时,它们各自的模板在DOM中重新排序。
  4. 否则,该项的DOM元素将保持不变。

以上的情况,*ngFor更新我们的视图都保持足够节源,是的,它应当这样。

但也有些例外,比如当我们改变heroes的引用

heroes = heroes.concat(newHero);

*ngFor将重新渲染所有DOM,即便它只是增加了一个英雄。这种情况尤其发生在我们的列表数据从服务器更新下来时,它的每一次更新都差不多是一次引用的转变。

Angular为我们提供了NgForTrackBy指令,它被设置为组件内部类的一个函数,该函数要求返回资源被判断为没有变化的依据。

*ngFor = "let hero of heroes trackBy:trackByHeroes"

trackByHeroes(index : number, hero : Hero){
    return hero.id;
}

它可以接收两个参数,当前项索引和当前项原值,这里我们返回英雄的id值,表明两个id值如果一致,则他们是同一个英雄,此时不需要完全重新渲染该dom,而是复用它。

2. 指令加*的原因

语法糖,将总是展开为模板,例如

<span *ngIf="show" other="other">example</span>

*语法糖直接展开为

<template [ngIf]="show">
    <span other="other">example</span>
</template>

其中,ngIf使用方括号使得show作为变量解析,而不是通常html语义上的直接属性。

什么情况下使用*语法糖?

所有直接性控制元素渲染与否的指令需要使用*,例如*ngFor*ngIf*ngSwitchCase*ngPluralCase,它们都将被包裹在template内,根据表达式值判定是否被渲染。而ngSwitchngPlural总是显示,它们的下级指令才直接控制渲染,所以不使用*语法糖。

其它情况的指令,一般遵守一个规则:

  1. 需要从元素流入数据到内部类,使用()包裹指令,例如所有事件都是从元素触发从而流入内部类引发控制,所以事件指令都是用()
  2. 需要从内部类流出数据到元素,使用[]包括指令,例如ngIfngForngSwitch等,它们都需要以内部类的变量为判断依据,所以都需要使用[],只是ngIfngFor等由于涉及控制元素是否渲染,它们需要template参与,所以提供了*语法糖简化编写,内部依然是[]
  3. 需要双向流动数据的,使用[()],例如[(ngModel)]就是一个典例。

2. 模板输入变量和模板引用变量

在模板上定义,作用域只在模板内部的普通变量称为模板输入变量。例如

<li *ngFor="let hero of heroes"></li>

其中

let hero

定义了一个模板输入变量,它不能在模板外部使用。

模板引用变量是模板中对 DOM 元素或指令的引用,可在同一元素、兄弟元素或任何子元素中被使用。例如

//原生dom对象,使用#定义
<span #cur></span>

//原生dom对象,使用规范的ref-定义
<span ref-cur></span>

//被Angular封装的form对象
<form #curForm="ngForm">

    <input name="name" required [(ngModel)]="name">

    //提交按钮被Angular内置的表单验证控制
    <input type="submit" [disable]="!curForm.form.valid" value="提交">
</form>

3. 安全导航操作符?.和管道操作符|

this is my name {{my.name}}

类似上面的表达式,my对象如果为null将导致应用报错。我们可以手动判断,例如

this is my name {{my?my.name:''}}

this is my name {{my && my.name}}

应对这种情况,Angular提供了比较优雅的一个表达式操作符?.,当遇到空值时跳出,避免应用出错。更重要的是,它非常适合多重路径的处理。

this is my name {{my?.name}}
this is the test {{a?.b?.c?.d}}

至于管道操作符,它可以一级一级的流动,还可以使用:添加管道条件。

4. Angular的插值表达式什么不被支持?

  • 赋值 (=, +=, -=, …)
  • new运算符
  • 使用;或,的链式表达式
  • 自增或自减操作符 (++和–)
  • 不支持位运算|和&
  • 具有新的模板表达式运算符,比如|和?.

5. 分清property属性绑定和Attribute绑定

//完全等同的两种绑定
![]({{img_url}})
<img [src]="img_url" />

Angular只允许绑定元素已有的原生属性,例如img的src属性,不存在的会报错。

<img [myAttr]="img_attr" />

这时候我们只能使用Attribute绑定:

<img [attr.myAttr]="img_attr" />

像这种绑定方式还有下面的场景:

<img [class]="imgClass" />
<img [class.imgClass]="true" />

//当然,我们跟倾向于使用ngClass来批量管理类名
<img [ngClass]="{imgClass : true}">

<span [style.color]="gray">cmx</span>
<span [style.background-color]="gray">cmx</span>

//跟单位
<span [style.font-size.em]="2">cmx</span>

//驼峰命名
<span [style.fontSize.em]="2">cmx</span>

//当然,我们还是有ngStyle作为批量管理的选择
<span [ngStyle]="{'font-size':'10px'}">cmx</span>

5. 路由返回两种方式

  1. 路由

    constructor(
     private router : Router,
     private route : ActivatedRoute
    ){}
    this.router.navigate(['../'],{relativeTo:this.route});
  2. location

    constructor(
     private location : Location
    ){}
    this.location.back();

6. 信任安全的值

angular2有自身的一套安全过滤系统,例如,动态绑定一个url,angular2会自动把它无害化,诸如使用:unsafe:xxx的手段。但有时候它会导致我们得不到预期的运行结果,例如当我们使用URL.createObjectUrl用于预览本地选择的图片时,直接将其对象赋值给img标签的src通常会由于该安全机制而失败。如果我们确信自己是对的,就有必要使用angular2提供的api信任它。

//注入DomSanitizer
constructor(
    private sanitizer : DomSanitizer
) { }

//根据需要调用下面的方法之一
sanitizer.bypassSecurityTrustHtml(html)
sanitizer.bypassSecurityTrustScript(script)
sanitizer.bypassSecurityTrustStyle(style)
sanitizer.bypassSecurityTrustUrl(url)
sanitizer.bypassSecurityTrustResourceUrl(rurl)

7. 编写一个图片加载完成的指令

目前的情况看来,angular2并没有提供图片load的事件绑定,有需要的话,自己编写也并不难

@Directive({
  selector: 'img[loaded]'
})
export class ImgLoadedDirective {

  @Input()
  loaded : any

  @Input()
  data : any

  constructor(renderer : Renderer, el : ElementRef) {
    renderer.listen(el.nativeElement, 'load', () => {
      this.loaded(this.data);
    });
  }

}

指令监听img标签的loaded属性,传入一个方法名,有必要的话还可以传入data,当img触发load事件时,会自动调用该方法并传入data。

//html
![](...)

//ts
load(data){
    dosomething;
}
IT文库 » angular2零碎剖析
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址