显示数据

显示数据

在 Angular 中最典型的数据显示方式,就是把 HTML 模板中的控件绑定到 Angular 组件的属性。

本章中,你将创建一个带英雄列表的组件。 你将显示英雄名字的列表,并根据条件在列表下方显示一条消息。

最终的用户界面是这样的:

这个在线例子 / 下载范例演示了本章中描述的所有语法和代码片段。

使用插值表达式显示组件属性

要显示组件的属性,最简单的方式就是通过插值表达式 (interpolation) 来绑定属性名。 要使用插值表达式,就把属性名包裹在双花括号里放进视图模板,如 {{myHero}}

按照快速起步的说明,创建一个新项目,名为displaying-data

删除 app.component.html 文件,这个范例中不再需要它了。

然后,到 app.component.ts 文件中修改组件的模板和代码。

修改完之后,它应该是这样的:

src/app/app.component.ts

content_copyimport { Component } from '@angular/core'; @Component{ selector: 'app-root', template: ` <h1>{{title}}</h1> <h2>My favorite hero is: {{myHero}}</h2> `})export class AppComponent { title = 'Tour of Heroes'; myHero = 'Windstorm';}

再把两个属性 titlemyHero 添加到之前空白的组件中。

修改完的模板会使用双花括号形式的插值表达式来显示这两个模板属性:

src/app/app.component.ts (template)

content_copytemplate: ` <h1>{{title}}</h1> <h2>My favorite hero is: {{myHero}}</h2> `

模板是包在 ECMAScript 2015 反引号 (`) 中的一个多行字符串。 反引号 (`) — 注意,不是单引号 (') — 允许把一个字符串写在多行上, 使 HTML 模板更容易阅读。

Angular 自动从组件中提取 titlemyHero 属性的值,并且把这些值插入浏览器中。当这些属性发生变化时,Angular 就会自动刷新显示。

严格来说,“重新显示”是在某些与视图有关的异步事件之后发生的,例如,按键、定时器完成或对 HTTP 请求的响应。

注意,你没有调用 new 来创建 AppComponent 类的实例,是 Angular 替你创建了它。那么它是如何创建的呢?

注意 @Component 装饰器中指定的 CSS 选择器 selector,它指定了一个叫 <app-root> 的元素。 该元素是 index.html 文件里的一个占位符。

src/index.html (body)

content_copy<body> <app-root></app-root> </body>

当你通过 main.ts 中的 AppComponent 类启动时,Angular 在 index.html 中查找一个 <app-root> 元素, 然后实例化一个 AppComponent,并将其渲染到 <app-root> 标签中。

运行应用。它应该显示出标题和英雄名:

回顾一下前面所做的决定,看看还有哪些其它选择。

内联 (inline) 模板还是模板文件?

你可以在两种地方存放组件模板。 你可以使用 template 属性把它定义为内联的,或者把模板定义在一个独立的 HTML 文件中, 再通过 @Component 装饰器中的 templateUrl 属性, 在组件元数据中把它链接到组件。

到底选择内联 HTML 还是独立 HTML 取决于个人喜好、具体状况和组织级策略。 上面的应用选择内联 HTML ,是因为模板很小,而且没有额外的 HTML 文件显得这个演示简单些。

无论用哪种风格,模板数据绑定在访问组件属性方面都是完全一样的。

默认情况下,Angular CLI 生成组件时会带有模板文件,你可以通过参数覆盖它:

content_copyng generate component hero -it

使用构造函数还是变量初始化?

虽然这个例子使用了变量赋值的方式初始化组件,你还可以使用构造函数来声明和初始化属性。

content_copyexport class AppCtorComponent { title: string; myHero: string; constructor() { this.title = 'Tour of Heroes'; this.myHero = 'Windstorm'; } }

为了让本应用更加简短,它采用了更简单的“变量赋值”风格。

使用 ngFor 显示数组属性

要显示一个英雄列表,先向组件中添加一个英雄名字数组,然后把 myHero重定义为数组中的第一个名字。

src/app/app.component.ts (class)

content_copyexport class AppComponent { title = 'Tour of Heroes'; heroes = ['Windstorm', 'Bombasto', 'Magneta', 'Tornado']; myHero = this.heroes[0]; }

接着,在模板中使用 Angular 的 ngFor 指令来显示 heroes 列表中的每一项。

src/app/app.component.ts (template)

content_copytemplate: ` <h1>{{title}}</h1> <h2>My favorite hero is: {{myHero}}</h2> <p>Heroes:</p> <ul> <li *ngFor="let hero of heroes"> {{ hero }} </li> </ul> `

这个界面使用了由 <ul> 和 <li> 标签组成的无序列表。<li> 元素里的 *ngFor 是 Angular 的“迭代”指令。 它将 <li> 元素及其子级标记为“迭代模板”:

src/app/app.component.ts (li)

content_copy<li *ngFor="let hero of heroes"> {{ hero }} </li>

不要忘记 *ngFor 中的前导星号 (*)。它是语法中不可或缺的一部分。 更多信息,见模板语法。

注意看 ngFor 双引号表达式中的 hero,它是一个模板输入变量。 更多模板输入变量的信息,见模板语法中的 微语法 (microsyntax)。

Angular 为列表中的每个条目复制一个 <li> 元素,在每个迭代中,把 hero变量设置为当前条目(英雄)。 Angular 把 hero 变量作为双花括号插值表达式的上下文。

本例中,ngFor 用于显示一个“数组”, 但 ngFor 可以为任何可迭代的 (iterable) 对象重复渲染条目。

现在,英雄们出现在了一个无序列表中。

为数据创建一个类

应用代码直接在组件内部直接定义了数据。 作为演示还可以,但它显然不是最佳实践。

现在使用的是到了一个字符串数组的绑定。在真实的应用中,大多是到一个对象数组的绑定。

要将此绑定转换成使用对象,需要把这个英雄名字数组变成 Hero 对象数组。但首先得有一个 Hero 类。

content_copyng generate class hero

代码如下:

src/app/hero.ts

content_copyexport class Hero { constructor( public id: number, public name: string) { } }

你定义了一个类,具有一个构造函数和两个属性:idname

它可能看上去不像是有属性的类,但它确实有,利用的是 TypeScript 提供的简写形式 —— 用构造函数的参数直接定义属性。

来看第一个参数:

src/app/hero.ts (id)

content_copypublic id: number,

这个简写语法做了很多:

  • 声明了一个构造函数参数及其类型。

使用 Hero 类

导入了 Hero 类之后,组件的 heroes 属性就可以返回一个类型化的Hero 对象数组了。

src/app/app.component.ts (heroes)

content_copyheroes = [ new Hero(1, 'Windstorm'), new Hero(13, 'Bombasto'), new Hero(15, 'Magneta'), new Hero(20, 'Tornado') ]; myHero = this.heroes[0];

接着,修改模板。 现在它显示的是英雄的 idname。 要修复它,只显示英雄的 name 属性就行了。

src/app/app.component.ts (template)

content_copytemplate: ` <h1>{{title}}</h1> <h2>My favorite hero is: {{myHero.name}}</h2> <p>Heroes:</p> <ul> <li *ngFor="let hero of heroes"> {{ hero.name }} </li> </ul> `

显示上还和以前一样,不过代码更清晰了。

通过 NgIf 进行条件显示

有时,应用需要只在特定情况下显示视图或视图的一部分。

来改一下这个例子,如果多于三位英雄,显示一条消息。

Angular 的 ngIf 指令会根据一个布尔条件来显示或移除一个元素。 来看看实际效果,把下列语句加到模板的底部:

src/app/app.component.ts (message)

content_copy<p *ngIf="heroes.length > 3">There are many heroes!</p>

不要忘了 *ngIf 中的前导星号 (*)。它是本语法中不可或缺的一部分。 更多 ngIf* 的内容,见模板语法中的ngIf

双引号中的模板表达式 *ngIf="heros.length > 3",外观和行为很象 TypeScript 。 当组件中的英雄列表有三个以上的条目时,Angular 把这个段落添加到 DOM 中,于是消息显示了出来。 更多信息,见模板语法中的模板表达式。

Angular 并不是在显示和隐藏这条消息,它是在从 DOM 中添加和移除这个段落元素。 这会提高性能,特别是在一些大的项目中有条件地包含或排除一大堆带着很多数据绑定的 HTML 时。

试一下。因为这个数组中有四个条目,所以消息应该显示出来。 回到 app.component.ts,从英雄数组中删除或注释掉一个元素。 浏览器应该自动刷新,消息应该会消失。

小结

现在你知道了如何使用:

  • 带有双花括号的插值表达式 (interpolation) 来显示一个组件属性。

下面是最终的代码:

src/app/app.component.ts

src/app/hero.ts

src/app/app.module.ts

main.ts

content_copyimport { Component } from '@angular/core'; import { Hero } from './hero'; @Component{ selector: 'app-root', template: ` <h1>{{title}}</h1> <h2>My favorite hero is: {{myHero.name}}</h2> <p>Heroes:</p> <ul> <li *ngFor="let hero of heroes"> {{ hero.name }} </li> </ul> <p *ngIf="heroes.length > 3">There are many heroes!</p>`})export class AppComponent { title = 'Tour of Heroes'; heroes = [ new Hero(1, 'Windstorm'), new Hero(13, 'Bombasto'), new Hero(15, 'Magneta'), new Hero(20, 'Tornado') ]; myHero = this.heroes[0];}