Angular 2的语法
大家好,我是拉科。在我前几天主持的活动中听到有人说”不知道Angular 2的*符号有什么意义,感觉很奇怪”,我觉得意外地很多人不知道,所以我决定写下来,希望能帮助大家更好地理解Angular 2。
另外,本文是基于Angular 2 Beta.14的版本。
Angular 2 中的指令
Angular 2主要以组件为导向,应用程序是由组件组装而成。但是,与Angular 1相同,我们仍然可以使用指令来修饰HTML元素和组件。
以下是myDirective指令,它可以更改应用于元素的样式,并将颜色设为红色。
import {Component, Directive, ElementRef, Renderer} from 'angular2/core'
@Directive({
selector: "[myDirective]"
})
class MyDirective {
constructor(
private el: ElementRef,
private renderer: Renderer
) {}
ngOnInit() {
this.renderer.setElementStyle(this.el.nativeElement, "color", "red");
}
}
@Component({
selector: 'my-app',
template: `
<div myDirective>Hello {{name}}</div>
`,
directives: [MyDirective]
})
export class App {
constructor() {
this.name = 'Angular2'
}
}
指令和组件一样,也有生命周期方法钩子。在构造函数的依赖注入中获取的实例将在ngOnInit中使用。
此外,指令的选择器元数据可以是myDirective元素,也可以是[myDirective]属性,当然还可以是.myDirective类。(虽然并不常知且并不推荐,但实际上组件的选择器也不一定要是元素。)
语法:模板化
在概述了指令的基本内容之后,我们将解释符号的使用。首先,让我们确认一下上面提到的myDirective指令实际上生成了怎样的DOM结构。通过查看Chrome开发者工具,我们可以看到如下的配置:在my-app元素中,模板被展开,并且在其中具有mydirective属性的div元素中应用了样式。
让我们用”*”符号来运行myDirective指令,看看会发生什么,这是直觉的,DOM结构没有任何异样。
@Component({
selector: 'my-app',
template: `
<div *myDirective>Hello {{name}}</div>
`,
directives: [MyDirective]
})
export class App {
constructor() {
this.name = 'Angular2'
}
}
真不可思议,div元素消失之前,留下了一个神秘的注释!
将要素转变为“模板化”。
当我给myDirective添加前缀时,div元素消失了。它并不仅仅是消失了,而是被“模板化”了。所谓“模板化”,就是将元素保存为一个模板,以便随时进行复制的机制。
让我们来看一下ngIf指令的源代码,作为一个简单易懂的例子。
@Directive({selector: '[ngIf]', inputs: ['ngIf']})
export class NgIf {
private _prevCondition: boolean = null;
constructor(private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef) {}
set ngIf(newCondition: any /* boolean */) {
if (newCondition && (isBlank(this._prevCondition) || !this._prevCondition)) {
this._prevCondition = true;
this._viewContainer.createEmbeddedView(this._templateRef);
} else if (!newCondition && (isBlank(this._prevCondition) || this._prevCondition)) {
this._prevCondition = false;
this._viewContainer.clear();
}
}
}
angular / ng_if.ts在angular / angular的主版本中
ngIf指令根据所给的值newCondition生成或删除元素。为了进行这种”生成”,需要模板。换句话说,*ngIf指令将附加到的元素保持为模板,并基于该模板复制并显示新元素。
*ngFor也以相同的机制工作。只是ngFor指令生成多个元素,本质上与ngIf没有什么不同。
当需要将指定的指令应用到元素本身作为模板时,就需要使用*语法对其进行模板化。
ViewContainerRef和TemplateRef
先前在给myDirective元素添加*前缀时,元素消失的原因是只对模板进行了标准化,但没有根据该模板创建元素。让我们尝试在myDirective中也使用模板吧!
模板化的元素可以作为TemplateRef类的实例进行依赖注入。TemplateRef只有带有*前缀才能进行依赖注入,所以基本上,指令要么使用TemplateRef,要么不使用,必须做出决定。(※也可以使用可选的依赖注入进行切换)
由于仅有模板不能生成元素,因此还需要进行依赖注入以根据模板创建元素。这就是 ViewContainerRef 的作用。ViewContainerRef 是一个用于将带有指令的元素视为容器的类。通过使用 ViewContainerRef 和 TemplateRef,可以将由模板生成的元素放置在容器中。
使用myDirective指令的下一个示例是为了生成两个相同的元素。
import {Component, Directive, ViewContainerRef, TemplateRef} from 'angular2/core'
@Directive({
selector: "[myDirective]"
})
class MyDirective {
constructor(
private _template: TemplateRef,
private _viewContainer: ViewContainerRef
) {}
ngOnInit() {
for(let i = 0; i < 2; i++) {
this._viewContainer.createEmbeddedView(this._template);
}
}
}
@Component({
selector: 'my-app',
template: `
<div *myDirective>Hello {{name}}</div>
`,
directives: [MyDirective]
})
export class App {
constructor() {
this.name = 'Angular2'
}
}
当您通过ViewContainerRef类的createEmbeddedView方法传递TemplateRef实例时,它会使用该模板生成元素并将其嵌入到容器中。生成的DOM在上述代码中将如下所示。
如您所见,与my-app的模板HTML完全不同的结构。使用模板化指令可以轻易地破坏组件定义的DOM结构。因此,除非明确地使用*语法,否则将无法获取TemplateRef。
使用模板的方法
顺便说一下,可以通过template元素来替换语法。也就是说,通过template元素可以实现模板化。
使用先前的*myDirective指示,可以通过使用template要素来写成以下内容。
@Component({
selector: 'my-app',
template: `
<template myDirective>
<div>Hello {{name}}</div>
</template>
`,
directives: [MyDirective]
})
export class App {
constructor() {
this.name = 'Angular2'
}
}
在template要素中添加没有前缀的myDirective,并在其中编写需要建立模板的HTML。这样就可以实现与之前完全相同的模板化。
总结
Angular 2的语法是用于将HTML元素模板化的工具,不仅可以使用内置的指令如*ngIf和*ngFor,还可以自己使用它来实现便利的功能。然而,要熟练掌握它并不容易,因此建议在掌握了通过组件构建视图的基础之后再学习这些中级技巧。
这次的示例代码可以在 Plunker 上找到。