*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语句中,如果在语句外部使用我们需要导出为局部变量。
{{index + ". " + hero.name}}
$implicit
为当前行本身,实际上就是我们这里的单个hero。其它几个属性都见名知意。
*ngFor
的语句实际上属于Angular特有的微语法,它被Angular自身解析。
*ngFor
正因为常常操作大量数据而应当在性能上被我们格外关注,首先*ngFor
的改变规则
以上的情况,*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,而是复用它。
*
的原因语法糖,将总是展开为模板,例如
example
*
语法糖直接展开为
example
其中,ngIf
使用方括号使得show
作为变量解析,而不是通常html
语义上的直接属性。
什么情况下使用*
语法糖?
所有直接性控制元素渲染与否的指令需要使用*
,例如*ngFor
、*ngIf
、*ngSwitchCase
、*ngPluralCase
,它们都将被包裹在template
内,根据表达式值判定是否被渲染。而ngSwitch
和ngPlural
总是显示,它们的下级指令才直接控制渲染,所以不使用*
语法糖。
其它情况的指令,一般遵守一个规则:
()
包裹指令,例如所有事件都是从元素触发从而流入内部类引发控制,所以事件指令都是用()
。[]
包括指令,例如ngIf
、ngFor
、ngSwitch
等,它们都需要以内部类的变量为判断依据,所以都需要使用[]
,只是ngIf
、ngFor
等由于涉及控制元素是否渲染,它们需要template
参与,所以提供了*
语法糖简化编写,内部依然是[]
。[()]
,例如[(ngModel)]
就是一个典例。在模板上定义,作用域只在模板内部的普通变量称为模板输入变量。例如
其中
let hero
定义了一个模板输入变量,它不能在模板外部使用。
模板引用变量是模板中对 DOM 元素或指令的引用,可在同一元素、兄弟元素或任何子元素中被使用。例如
//原生dom对象,使用#定义
//原生dom对象,使用规范的ref-定义
//被Angular封装的form对象
?.
和管道操作符|
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}}
至于管道操作符,它可以一级一级的流动,还可以使用:
添加管道条件。
除以上官方明确说明的不被支持,实际上也有更多是不支持的,例如console、typeof/instanceof操作符等。但我们能做到吗?当然可以,在内部类声明方法即可,模板可以调用内部类方法,所以也就解开了插值表达式的限制。例如我们需要判断是否数组:
//*.component.ts
isArray(arr){
return Array.isArray(arr);
}
//*.component.html
*ngIf="isArray(arr)"
//完全等同的两种绑定
Angular
只允许绑定元素已有的原生属性,例如img的src属性,不存在的会报错。
这时候我们只能使用Attribute
绑定:
像这种绑定方式还有下面的场景:
//当然,我们更倾向于使用ngClass来批量管理类名
cmx
cmx
//跟单位
cmx
//驼峰命名
cmx
//当然,我们还是有ngStyle作为批量管理的选择
cmx
constructor(
private router : Router,
private route : ActivatedRoute
){}
this.router.navigate(['../'],{relativeTo:this.route});
constructor(
private location : Location
){}
this.location.back();
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)
目前的情况看来,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;
}
在一个*ngFor中,如果同时使用了*ngIf将导致转化为template出错,那么*ngFor中我们如果也想条件性渲染部分跳过呢?放弃语法糖可以做到
{{hero.name}}
/*====================*/
//只显示cmx除外的英雄
{{hero.name}}
angular2默认采用HTML5的pushState来管理路由,它会导致前端路由与后端路由的冲突,例如当部署到nginx环境时,我们通过首页进入子路由一切正常,但是在子路由路径下,刷新就会报404了。默认情况下nginx会当成这个路径是实际web路径下的资源而去定位它,但可想而知实际是并不存在的。折中的方案可以改回hash风格:
RouterModule.forRoot(routes, {useHash: true})