Angular是一个使用HTML和JavaScript构建客户端应用的框架。
简单来说,Angular应用由使用Angular式的标记语言写的HTML模板组成,使用conpoment类管理这些模板,在service中添加业务逻辑,使用bootstrapper管理根组件。
Angular应用是模块化的。通常将应用分成很多模块。一个模块是专注于某一个功能的一段代码。模块通常export代码中的一些值,尤其是一个类。
// app/app.component.ts
export class AppComponent { }
export表示这个文件是一个模块,且模块的AppComponent
类是public的,可以被其他模块访问。其他模块访问时需要import:
// app/main.ts
import {AppComponent} from './app.component';
import告诉系统可以从同文件夹下的app.component
模块中获取AppComponent
。模块名称(也称为模块ID)也就是不包含扩展名的文件名。
Library Module
有的模块是包含包含多个模块的库。Angular提供了一系列库模块,每个模块实际上是对多个逻辑上相关的私有模块包装后提供的公共外观模式。例如angular2/core
、angular2/common
、angular2/router
、angular2/http
。
从Angular库模块中引入一些东西的格式是这样的:
import {Component} from 'angular2/core';
之前引入AppComponent
组件是这样的:
import {AppComponent} from './app.component';
可以看到,引入库模块时直接指定模块名称就行了,不需要在前面加路径。但是从我们自己的模块文件中引入时就要在模块名称前带上文件路径。
组件控制屏幕的部分显示内容(称之为View视图)。组件的应用逻辑定义在类里面,通过属性和方法与视图交互。
// app/hero-list.component.ts
export class HeroListComponent implements OnInit {
constructor(private _service: HeroService){ }
heroes:Hero[];
selectedHero: Hero;
ngOnInit(){
this.heroes = this._service.getHeroes();
}
selectHero(hero: Hero) { this.selectedHero = hero; }
}
Angular随着用户的操作自动创建、更新、销毁组件。可以使用Lifecycle Hooks介入组件生命周期的每个阶段。
组件的视图是用模板定义的。
// app/hero-list.component.html
<h2>Hero List</h2>
<p><i>Pick a hero from the list</i></p>
<div *ngFor="#hero of heroes" (click)="selectHero(hero)">
{{hero.name}}
</div>
<hero-detail *ngIf="selectedHero" [hero]="selectedHero"></hero-detail>
模板中包含一些Angular模板语法,如*ngFor
、{{hero.name}}
、(click)
、[hero]
、<hero-detail>
。其中<hero-detail>
标签是一个自定义元素,表示HeroDetailComponent
组件。也就是说,HeroDetailComponent
是HeroListComponent
的子组件。
元数据用于告诉Angular如何处理一个类。最简单的方式是使用装饰器来为类附加元数据。例如使用@Component
装饰器将HeroesComponent
类标识为组件类:
// app/hero-list.component.ts
@Component({
selector: 'hero-list',
templateUrl: 'app/hero-list.component.html',
directives: [HeroDetailComponent],
providers: [HeroService]
})
export class HeroesComponent { ... }
装饰器是一个函数,通常接收一个配置参数。
@Component
的配置项有:
selector
:一个css选择器,告诉Angular发现<hero-list>
后,创建组件实例并将它添加到<hero-list>
内。templateUrl
:组件的模板地址。directives
:数组中包含了模板所需的组件及指令。providers
:数组中包含了组件所需的服务的依赖注入提供者。@Component
函数接收配置对象参数后,将其转换成它所关联的组件类的元数据。
模板、元数据、组件三者加在一起描述了视图:
其它常用的元数据装饰器还有@Injectable
、@Input
、@Output
、@RouterConfig
。
Angular使用数据绑定将模板部分和组件部分相关联。
// app/hero-list.component.html
<div>{{hero.name}}</div>
<hero-detail [hero]="selectedHero"></hero-detail>
<div (click)="selectHero(hero)"></div>
<input [(ngModel)]="hero.name">
数据绑定语法有四种:
hero.name
属性值放到<div>
标签中。HeroListComponent
的selectedHero
传递给子组件HeroDetailComponent
的hero
属性。selectHero
方法ngModel
指令将属性绑定和事件绑定合并在一个标识中。Angular在每次Javascript事件循环中处理一次所有的绑定,从应用的组件树的根开始深度优先遍历。
Angular模板是动态的。当Angular渲染模板时是根据指令将他们转换成DOM。
指令是一个包含指令元数据的类。可以使用@Directive
装饰器来为类附加元数据。
组件也是指令,它是一个包含模板的指令。@Component
是扩展了面向模板特性的@Directive
。
还有两种类型的指令:结构指令和属性指令:
结构指令通过添加、移除、替换DOM中的元素来改变视图。例如:
<div *ngFor="#hero of heroes"></div>
<hero-detail *ngIf="selectedHero"></hero-detail>
*ngFor
告诉Angular为heroes
数组的每个元素创建一个<div>
。*ngIf
表示当selectedHero
存在时,就创建一个HeroDetail
组件。属性指令用来修改已存在的元素的外观或行为。在模板中它们看起来就像普通的HTML属性。例如,ngModel
指令用于双向数据绑定:
<input [(ngModel)]="hero.name">
Angular提供了ngSwitch
(结构属性)和ngStyle
、ngClass
(属性指令)等。
服务是应用所需的一些值、函数或功能等。Angular本身并没有定义service。
// app/logger.service.ts
export class Logger {
log(msg: any) { console.log(msg); }
error(msg: any) { console.error(msg); }
warn(msg: any) { console.warn(msg); }
}
// app/hero.service.ts
export class HeroService {
constructor(
private _backend: BackendService,
private _logger: Logger) { }
private _heroes:Hero[] = [];
getHeroes() {
this._backend.getAll(Hero).then( (heroes:Hero[]) => {
this._logger.log(`Fetched ${heroes.length} heroes.`);
this._heroes.push(...heroes); // fill cache
});
return this._heroes;
}
}
组件是服务的消费者。组件依赖服务来处理大部分事情。组件是视图和应用逻辑的中介者,一个好的组件应该只包含用于数据绑定的属性和方法,它将逻辑处理委托给服务。
在TypeScript中,Angular可以根据构造函数的参数类型分辨所需要的服务组件。例如:
// app/hero-list.component
constructor(private _service: HeroService){ }
当Angular创建组件时,它向Injector询问组件所需的服务。Injector
维护一个它创建的服务实例的容器。当请求的服务实例不在容器中时,injector就创建一个并添加到容器中,然后将服务返回给Angular。当所有所需的服务都返回了,Angular就可以用这些服务作为参数调用组件的构造函数了。
如果Injector
不包含一个服务时,它怎么知道如何创建该服务呢?因此,需要在这之前为Injector
注册一个该服务的提供者。提供者是一个可以创建服务的某个东西,通常是服务本身。
可以在组件树的任意一层注册提供者。例如在启动根组件时注册,这样所有地方可以共享同一个服务实例:
// app/main.ts
bootstrap(AppComponent, [BackendService, HeroService, Logger]);
也可以在组件注册,这样每个组件实例有一个服务实例:
// app/hero-list.component.ts
@Component({
providers: [HeroService]
})
export class HeroesComponent { ... }
参考资料
Angular2官方文档