组件简介

组件简介

组件控制屏幕上被称为视图的一小片区域。比如,教程中的下列视图都是由一个个组件所定义和控制的:

  • 带有导航链接的应用根组件。

你在类中定义组件的应用逻辑,为视图提供支持。 组件通过一些由属性和方法组成的 API 与视图交互。

比如,HeroListComponent 有一个 heroes 属性,它会返回一个从服务中取到的英雄数组。 HeroListComponent 还有一个 selectHero() 方法,当用户从列表中选择一个英雄时,它会设置 selectedHero 属性的值。 该组件会从服务获取英雄列表,它是一个 TypeScript 的构造器参数型属性。本服务通过依赖注入系统提供给该组件。

src/app/hero-list.component.ts (class)

content_copyexport class HeroListComponent implements OnInit { heroes: Hero[]; selectedHero: Hero; constructor(private service: HeroService) { } ngOnInit() { this.heroes = this.service.getHeroes( } selectHero(hero: Hero) { this.selectedHero = hero; } }

当用户在应用中穿行时,Angular 就会创建、更新、销毁一些组件。 你的应用可以通过一些可选的生命周期钩子(比如ngOnInit())来在每个特定的时机采取行动。

组件的元数据

@Component 装饰器会指出紧随其后的那个类是个组件类,并为其指定元数据。 在下面的范例代码中,你可以看到 HeroListComponent只是一个普通类,完全没有 Angular 特有的标记或语法。 直到给它加上了 @Component 装饰器,它才变成了组件。

组件的元数据告诉 Angular 到哪里获取它需要的主要构造块,以创建和展示这个组件及其视图。 具体来说,它把一个模板(无论是直接内联在代码中还是引用的外部文件)和该组件关联起来。 该组件及其模板,共同描述了一个视图

除了包含或指向模板之外,@Component 的元数据还会配置要如何在 HTML 中引用该组件,以及该组件需要哪些服务等等。

下面的例子中就是 HeroListComponent 的基础元数据:

src/app/hero-list.component.ts (metadata)

content_copy@Component{ selector: 'app-hero-list', templateUrl: './hero-list.component.html', providers: [ HeroService ] }) export class HeroListComponent implements OnInit { /* . . . */ }

这个例子展示了一些最常用的 @Component 配置选项:

  • selector:是一个 CSS 选择器,它会告诉 Angular,一旦在模板 HTML 中找到了这个选择器对应的标签,就创建并插入该组件的一个实例。 比如,如果应用的 HTML 中包含 <app-hero-list></app-hero-list>,Angular 就会在这些标签中插入一个 HeroListComponent实例的视图。

模板与视图

你要通过组件的配套模板来定义其视图。模板就是一种 HTML,它会告诉 Angular 如何渲染该组件。

视图通常会分层次进行组织,让你能以 UI 分区或页面为单位进行修改、显示或隐藏。 与组件直接关联的模板会定义该组件的宿主视图。该组件还可以定义一个带层次结构的视图,它包含一些内嵌的视图作为其它组件的宿主。

带层次结构的视图可以包含同一模块(NgModule)中组件的视图,也可以(而且经常会)包含其它模块中定义的组件的视图。

模板语法

模板很像标准的 HTML,但是它还包含 Angular 的模板语法,这些模板语法可以根据你的应用逻辑、应用状态和 DOM 数据来修改这些 HTML。 你的模板可以使用数据绑定来协调应用和 DOM 中的数据,使用管道在显示出来之前对其进行转换,使用指令来把程序逻辑应用到要显示的内容上。

比如,下面是本教程中 HeroListComponent 的模板:

src/app/hero-list.component.html

content_copy<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> <app-hero-detail *ngIf="selectedHero" [hero]="selectedHero"></app-hero-detail>

这个模板使用了典型的 HTML 元素,比如 <h2> 和 <p>,还包括一些 Angular 的模板语法元素,如 *ngFor,{{hero.name}},click、[hero] 和 <app-hero-detail>。这些模板语法元素告诉 Angular 该如何根据程序逻辑和数据在屏幕上渲染 HTML。

  • *ngFor 指令告诉 Angular 在一个列表上进行迭代。

数据绑定

如果没有框架,你就要自己负责把数据值推送到 HTML 控件中,并把来自用户的响应转换成动作和对值的更新。 手动写这种数据推拉逻辑会很枯燥、容易出错,难以阅读 —— 用过 jQuery 的程序员一定深有体会。

Angular 支持双向数据绑定,这是一种对模板中的各个部件与组件中的各个部件进行协调的机制。 往模板 HTML 中添加绑定标记可以告诉 Angular 该如何连接它们。

下图显示了数据绑定标记的四种形式。每种形式都有一个方向 —— 从组件到 DOM、从 DOM 到组件或双向。

这个来自 HeroListComponent 模板中的例子使用了其中的三种形式:

src/app/hero-list.component.html (binding)

content_copy<li>{{hero.name}}</li> <app-hero-detail [hero]="selectedHero"></app-hero-detail> <li (click)="selectHero(hero)"></li>

  • {{hero.name}}插值表达式在 <li> 标签中显示组件的 hero.name 属性的值。

第四种重要的绑定形式是双向数据绑定,它把属性绑定和事件绑定组合成一种单独的写法。下面这个来自 HeroDetailComponent 模板中的例子通过 ngModel 指令使用了双向数据绑定

src/app/hero-detail.component.html (ngModel)

content_copy<input [(ngModel)]="hero.name">

在双向绑定中,数据属性值通过属性绑定从组件流到输入框。用户的修改通过事件绑定流回组件,把属性值设置为最新的值。

Angular 在每个 JavaScript 事件循环中处理所有的数据绑定,它会从组件树的根部开始,递归处理全部子组件。

数据绑定在模板及其组件之间的通讯中扮演了非常重要的角色,它对于父组件和子组件之间的通讯也同样重要。

管道

Angular 的管道可以让你在模板中声明显示值的转换逻辑。 带有 @Pipe 装饰器的类中会定义一个转换函数,用来把输入值转换成供视图显示用的输出值。

Angular 自带了很多管道,比如 date 管道和 currency 管道,完整的列表参见 Pipes API 列表。你也可以自己定义一些新管道。

要在 HTML 模板中指定值的转换方式,请使用 管道操作符 (|)

{{interpolated_value | pipe_name}}

你可以把管道串联起来,把一个管道函数的输出送给另一个管道函数进行转换。 管道还能接收一些参数,来控制它该如何进行转换。比如,你可以把要使用的日期格式传给 date 管道:

content_copy<!-- Default format: output 'Jun 15, 2015'--> <p>Today is {{today | date}}</p> <!-- fullDate format: output 'Monday, June 15, 2015'--> <p>The date is {{today | date:'fullDate'}}</p> <!-- shortTime format: output '9:43 AM'--> <p>The time is {{today | date:'shortTime'}}</p>

指令

Angular 的模板是动态的。当 Angular 渲染它们的时候,会根据指令给出的指示对 DOM 进行转换。 指令就是一个带有 @Directive 装饰器的类。

组件从技术角度上说就是一个指令,但是由于组件对 Angular 应用来说非常独特、非常重要,因此 Angular 专门定义了 @Component 装饰器,它使用一些面向模板的特性扩展了 @Directive 装饰器。

除组件外,还有两种指令:结构型指令属性型指令。和组件一样,指令的元数据把指令类和一个 selector 关联起来,selector 用来把该指令插入到 HTML 中。 在模板中,指令通常作为属性出现在元素标签上,可能仅仅作为名字出现,也可能作为赋值目标或绑定目标出现。

结构型指令

结构型指令通过添加、移除或替换 DOM 元素来修改布局。 这个范例模板使用了两个内置的结构型指令来为要渲染的视图添加程序逻辑:

src/app/hero-list.component.html (structural)

content_copy<li *ngFor="let hero of heroes"></li> <app-hero-detail *ngIf="selectedHero"></app-hero-detail>

  • *ngFor 是一个迭代器,它要求 Angular 为 heroes 列表中的每个 <li>渲染出一个 <li>。

属性型指令

属性型指令会修改现有元素的外观或行为。 在模板中,它们看起来就像普通的 HTML 属性一样,因此得名“属性型指令”。

ngModel 指令就是属性型指令的一个例子,它实现了双向数据绑定。ngModel 修改现有元素(一般是 <input>)的行为:设置其显示属性值,并响应 change 事件。

src/app/hero-detail.component.html (ngModel)

content_copy<input [(ngModel)]="hero.name">

Angular 还有很多预定义指令,它们或者修改布局结构(比如 ngSwitch),或者修改 DOM 元素和组件的某些方面(比如 ngStylengClass)。

你还可以写自己的指令。像 HeroListComponent 这样的组件就是一种自定义指令,你还可以创建自定义的结构型指令和属性型指令。