在 angular2 里,我们介绍了一个新概念叫"structural directives",用来描述那些根据表达式在 DOM 上或增加、或删除元素的指令。和其他指令不同,"structural directive"要么作用在template tag上、 要么配合template attribute使用、要么前缀"*"作为简写语法糖。因为这个新语法特性,初学者常常犯错。
你能分辨出来以下错误么?
错误的 ngFor 用法
// a: <div *ngFor="#item in items"> <p> {{ item }} </p> </div> // b: <template *ngFor #item [ngForOf]="items"> <p> {{ item }} </p> </template> // c: <div *ngFor="#item of items; trackBy=myTrackBy; #i=index"> <p>{{i}}: {{item}} </p> </div>
来,一步步解决错误
5a:把"in"换成"of"
// incorrect <div *ngFor="#item in items"> <p> {{ item }} </p> </div>
如果有 AngularJS 1 经验,通常很容易犯这个错。在 AngularJS 1 里,相同的repeater写作 ng-repeat="item in items" 。
angular2 将"in"换成"of"是为了和ES6中的 for-of 循环保持一致。也需要记住的是,如果不用"*"语法糖,那么完整的repeater写法要写作 ngForOf , 而非 ngForIn
// correct <div *ngFor="#item of items"> <p> {{ item }} </p> </div>
5b:语法糖和完整语法混着写
// incorrect <template *ngFor #item [ngForOf]="items"> <p> {{ item }} </p> </template>
混着写是没必要的 - 而且事实上,这么写也不工作。当你用了语法糖(前缀"*")以后, angular2 就会把她当成一个template attribute,而不是一般的指令。具体来说,解析器拿到了 ngFor 后面的字符串, 在字符串前面加上 ngFor ,然后当作template attribute来解析。如下代码:
<div *ngFor="#item of items">
会被当成这样:
<div template="ngFor #item of items">
当你混着写时,他其实变成了这样:
<template template="ngFor" #item [ngForOf]="items">
从template attribute角度分析,发现template attribute后面就只有一个 ngFor ,别的什么都没了。那必然解析不会正确,也不会正常运行了。
如果从从template tag角度分析,他又缺了一个 ngFor 指令,所以也会报错。没了 ngFor 指令, ngForOf 都不知道该对谁负责了。
可以这样修正,要么去掉"*"写完整格式,要么就完全按照"*"语法糖简写方式书写
// correct <template ngFor #item [ngForOf]="items"> <p> {{ item }} </p> </template> // correct <p *ngFor="#item of items"> {{ item }} </p>
5c:在简写形式里用了错误的操作符
// incorrect <div *ngFor="#item of items; trackBy=myTrackBy; #i=index"> <p>{{i}}: {{item}} </p> </div>
为了解释这儿到底出了什么错,我们先不用简写形式把代码写对了看看什么样子:
// correct <template ngFor #item [ngForOf]="items" [ngForTrackBy]="myTrackBy" #i="index"> <p> {{i}}: {{item}} </p> </template>
在完整形式下,结构还是很好理解的,我们来试着分解一下:
-
我们通过输入属性向 ngFor 里传入了两组数据:
-
绑定在 ngForOf 上的原始数据集合 items
-
绑定在 ngForTrackBy 上的自定义track-by函数
-
-
用 # 声明了两个 local template variables ,分别是: #i 和 #item 。 ngFor 指令在遍历 items 时,给这两个变量赋值
-
i 是从0开始的 items 每个元素的下标
-
item 是对应每个下标的元素
-
当我们通过"*"语法糖简写代码时,必须遵守如下原则,以便解析器能够理解简写语法:
-
所有配置都要写在 *ngFor 的属性值里
-
通过 = 操作符设置 local variable
-
通过 : 操作符设置input properties
-
去掉input properties里的 ngFor 前缀,譬如: ngForOf ,就只写成 of 就可以了
-
用分号做分隔
按照上述规范,代码修改如下:
// correct <p *ngFor="#item; of:items; trackBy:myTrackBy; #i=index"> {{i}}: {{item}} </p>
分号和冒号其实是可选的,解析器会忽略它们。写上仅仅是为了提高代码可读性。因此,也可以再省略一点点:
// correct <p *ngFor="#item of items; trackBy:myTrackBy; #i=index"> {{i}}: {{item}} </p>