Angular
主要分为八大构造块(也就是八个核心概念):模块 (module)、组件 (component)、模板 (template)、元数据 (metadata)、数据绑定 (data binding)、指令 (directive)、服务 (service)、依赖注入 (dependency injection)。其中,最核心的一个概念就就组件。
1. 模块 (module)
Angular
应用是模块化的,并且 Angular
有自己的模块系统,它被称为 Angular
模块或 NgModules
。
每个Angular
应用至少有一个模块(根模块),习惯上命名为AppModule
。
根模块在一些小型应用中可能是唯一的模块,大多数应用会有很多特性模块,每个模块都是一个内聚的代码块专注于某个应用领域、工作流或紧密相关的功能。
Angular
模块(无论是根模块还是特性模块)都是一个带有@NgModule
装饰器的类。
下面是一个简单的根模块:
// src/app/app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
@NgModule({
imports: [ BrowserModule ],
providers: [ Logger ],
declarations: [ AppComponent ],
exports: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
其中重要的属性是:
-
declarations
- 声明本模块中拥有的视图类。Angular
有三种视图类:组件、指令、管道; -
exports
-declarations
的子集,可用于其它模块的组件; -
imports
- 本模块声明的组件模板需要的类所在的其它模块。用来导入其他自定义模块,第三方插件模块; -
providers
- 服务的创建者,并加入到全局服务列表中,可用于应用任何部分; -
bootstrap
- 指定应用的主视图(称为根组件),它是所有其它视图的宿主。只有根模块才能设置bootstrap
属性。通常在main.ts
中引导AppModule
,这样platformBrowserDynamic().bootstrapModule(AppModule)
2. 组件 (component)
组件负责控制屏幕上的一小块区域,我们称之为视图。
下面是一个组件的简单例子:
// src/app/hero-list.component.ts
export class HeroListComponent implements OnInit {
heroes: Hero[];
selectedHero: Hero;
constructor(private service: HeroService) { }
ngOnInit() {
this.heroes = this.service.getHeroes();
}
selectHero(hero: Hero) { this.selectedHero = hero; }
}
3. 模板 (template)
模板就是HTML文件,但是不是标准的HTML文件,它使用了一些模板语法,模板语法使模板有了自己的逻辑关系,并能够实现和组件的简单数据交互。
下面是一个简单的模板:
// src/app/hero-list.component.html
<h2>Hero List</h2>
<p><i>Pick a hero from the list</i></p>
<ul>
<li *ngFor="let hero of heroes" (click)="selectHero(hero)">
{{hero.name}}
</li>
</ul>
<hero-detail *ngIf="selectedHero" [hero]="selectedHero"></hero-detail>
通常使用ng g component my-component
命令产生一个组件包含四个文件:
my-component.css // 样式文件
my-component.thml // 模板
my-component.spec.ts // 测试文件
my-component.ts // 这是组件? 通常我们认为这四个文件组成一个组件
4. 元数据 (metadata)
元数据告诉你如何处理一个类。
其实,在Angular
中每个组件只是一个类,但是我们可以通过装饰器来附加元数据告诉Angular
这是一个组件。
下面就是HeroListComponent
的一些元数据。
// src/app/hero-list.component.ts (metadata)
@Component({ // @Component 将后面的 HeroListComponent 类标记为一个组件
selector: 'hero-list',
templateUrl: './hero-list.component.html',
providers: [ HeroService ]
})
export class HeroListComponent implements OnInit {
/* . . . */
}
@Component
装饰器能接受一个配置对象, Angular 会基于这些信息创建和展示组件及其视图。
@Component
的配置项包括:
-
selector
:CSS
选择器,它告诉Angular
在父级HTML
中查找<hero-list>
标签,创建并插入该组件。 例如,如果应用的HTML
包含<hero-list></hero-list>
,Angular
就会把HeroListComponent
的一个实例插入到这个标签中; -
templateUrl
:组件HTML
模板的模块相对地址; -
providers
- 组件所需服务的依赖注入提供商数组。 这是在告诉Angular
:该组件的构造函数需要一个HeroService
服务,这样组件就可以从服务中获得英雄数据。
到这里你应该可以明白:模板、元数据和组件共同描绘出这个视图。
5. 数据绑定 (databinding)
数据绑定是Angular
中最常用的数据处理模式。数据绑定在模板与对应组件的交互中扮演了重要的角色,在父组件与子组件的通讯中也同样重要。
下面是一个简单的例子:
// src/app/hero-list.component.html
<li>{{hero.name}}</li>
<hero-detail [hero]="selectedHero"></hero-detail>
<li (click)="selectHero(hero)"></li>
-
{{hero.name}}
插值表达式在<li>
标签中显示组件的hero.name
属性的值; -
[hero]
属性绑定把父组件HeroListComponent
的selectedHero
的值传到子组件HeroDetailComponent
的hero
属性中; -
(click)
事件绑定在用户点击英雄的名字时调用组件的selectHero
方法。
Angular
默认没有双向绑定,但是,官方推荐这样来实现双向绑定:
<input [(ngModel)]="hero.name">
Angular
在每个 JavaScript
事件循环中处理所有的数据绑定,它会从组件树的根部开始,递归处理全部子组件。
6. 指令 (directive)
由于Angular
模板是动态的,所以你需要通过指令实现对DOM
的转换。(组件是一个带模板的指令;@Component
装饰器实际上就是一个@Directive
装饰器,只是扩展了一些面向模板的特性。 )
指令分为两种:结构指令、属性指令。
a. 结构指令: 通过在 DOM 中添加、移除和替换元素来修改布局。
下面是一个简单的内置结构指令的例子:
<!-- src/app/hero-list.component.html (structural) -->
<li *ngFor="let hero of heroes"></li>
<hero-detail *ngIf="selectedHero"></hero-detail>
-
*ngFor
告诉 Angular 为heroes
列表中的每个英雄生成一个<li>
标签。 -
*ngIf
表示只有在选择的英雄存在时,才会包含HeroDetail
组件。
b. 属性指令:修改一个现有元素的外观或行为。
简单例子:
<!-- src/app/hero-detail.component.html -->
<input [(ngModel)]="hero.name">
7. 服务 (service)
服务是一个广义范畴,包括:值、函数,或应用所需的特性。几乎任何东西都可以是一个服务。 典型的服务是一个类,具有专注的、明确的用途。它应该做一件特定的事情,并把它做好。
例如:
- 日志服务
- 数据服务
- 消息总线
- 税款计算器
- 应用程序配置
组件类应保持精简。组件本身不从服务器获得数据、不进行验证输入,也不直接往控制台写日志。 它们把这些任务委托给服务。所以说服务是跑腿的,服务一般用来处理业务逻辑,被注入在组件当中,服务是全局单例的。也就是说注入到所有组件中的服务是同一个。
一个简单的例子:
// src/app/hero.service.ts
export class HeroService {
private heroes: Hero[] = [];
constructor(
private backend: BackendService,
private logger: Logger) { }
getHeroes() {
this.backend.getAll(Hero).then( (heroes: Hero[]) => {
this.logger.log(`Fetched ${heroes.length} heroes.`);
this.heroes.push(...heroes); // fill cache
});
return this.heroes;
}
}
8. 依赖注入 (dependency injection)
“依赖注入”是提供类的新实例的一种方式,还负责处理好类所需的全部依赖。大多数依赖都是服务。 Angular
使用依赖注入来提供新组件以及组件所需的服务。
来看注入方式:
// src/app/hero-list.component.ts
constructor(private service: HeroService) { }
注入器维护了一个服务实例的容器,存放着以前创建的实例。 如果所请求的服务实例不在容器中,注入器就会创建一个服务实例,并且添加到容器中,然后把这个服务返回给 Angular。 当所有请求的服务都被解析完并返回时,Angular 会以这些服务为参数去调用组件的构造函数。 这就是依赖注入 。
通常我们将服务声明在根模块,以便在整个应用中使用这个服务。
// src/app/app.module.ts
providers: [
BackendService,
HeroService,
Logger
],
也可以在其他组件中声明服务,那么这个服务只能用于当前组件。把它注册在组件级表示该组件的每一个新实例都会有一个服务的新实例。
// src/app/hero-list.component.ts
@Component({
selector: 'hero-list',
templateUrl: './hero-list.component.html',
providers: [ HeroService ]
})
网友评论