通过把 Angular 组件的属性绑定到 HTML 模板的控件上,我们可以展示数据。
本小节中,我们将会创建一个英雄列表组件。我们将展示英雄名字的列表,并根据条件来决定是否在列表下方展示一条信息。
最终的 效果是这样的:
在线例子 / 下载示例代码 演示了这一节所有的提到的代码片段和语法。
1.用插值表达式展示组件的属性
通过插值表达式来绑定属性名,是展示组件属性最简单的方式。使用双花括号的插值表达式{{myHero}} ,我们可以把属性名myHero@H_403_13@ 放置在视图模板中。
(通过 Angular CLI 命令:ng new displaying-data@H_403_13@ )创建一个名为
displaying-data@H_403_13@ 的新项目,然后修改
app.component.ts@H_403_13@文件,改成如下代码所示:
src/app/app.component.ts
import { 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';
}
我们添加了两个属性(title@H_403_13@ 和
myHero@H_403_13@)到原先空的组件中。
通过使用插值表达式,更改后的模板展示组件的两个属性:
src/app/app.component.ts (template)
template: ` <h1>{{title}}</h1> <h2>My favorite hero is: {{myHero}}</h2> `
模板是ES6标准规定的以反引号(`) 括起来的多行字符串。反引号(`)与普通的单引号(’)不同,它允许多行书写,这使得代码可读性更好。
Angular 会自动从组件中获取title@H_403_13@ 和
myHero@H_403_13@ 这两个属性的值,把这些值传递给浏览器。 当这些属性改变时,Angular 会更新显示的值。
更准确地说,更新发生在一些与视图有关的异步事件之后,比如说键盘事件、计时器结束、或者一个 HTTP 请求。
注意,我们不会直接调用 new@H_403_13@ 来创建
appComponent@H_403_13@ 类的实例。Angular 会为我们创建这个实例。它是怎么做到的呢?
@Component@H_403_13@ 装饰器中的 CSS
selector@H_403_13@ 指定了一个名为
<app-root>@H_403_13@的元素。这个元素在
index.html@H_403_13@文件中是一个占位符:
src/index.html (body)
<body>
<app-root>loading...</app-root>
</body>
当我们在main.ts@H_403_13@中设置
bootstrap@H_403_13@为
AppComponent@H_403_13@类后,Angular 会在
index.html@H_403_13@ 中寻找
<app-root>@H_403_13@ , 找到后,会实例化
AppComponent@H_403_13@ ,并把它渲染到这个标签内。
运行这个项目(npm start@H_403_13@)。 应该能够看到如下所示的内容:
2.行内模板 or 模板文件?
组件的模板可在两个地方中选择一个存放。我们可以使用template@H_403_13@属性来定义模板为行内模板,或者在一个单独的 HTML 文件中定义模板,然后使用
@Component@H_403_13@ 装饰器的
templateUrl@H_403_13@ 属性,把它链接到组件的
元数据@H_403_13@中。
选择行内模板还是单独的 HTML 文件取决于 个人的代码风格、开发环境 或者 公司机构的代码规范。本章节的示例代码使用的是 行内的 HTML 模板, 因为这个模板很小。没有额外的 HTML 文件,这个 demo 更简单。
3.构造函数 or 变量实例化?
虽然,这个例子使用 变量赋值@H_403_13@来初始化组件,你也可以通过
构造函数@H_403_13@来声明并实例化属性。
src/app/app-ctor.component.ts (class)
export class AppCtorComponent {
title: string;
myHero: string;
constructor() {
this.title = 'Tour of Heroes';
this.myHero = 'Windstorm';
}
}
为了简便起见,本节中的示例代码,会更多地使用精简的变量赋值@H_403_13@的形式。
4.*ngFor@H_403_13@ 显示数组的属性
为了展示一个英雄列表,我们增加一个英雄名字的数组到组件中,并重新定义myHero@H_403_13@作为数组中的第一项。
src/app/app.component.ts (class)
export class AppComponent {
title = 'Tour of Heroes';
heroes = ['Windstorm','Bombasto','Magneta','Tornado'];
myHero = this.heroes[0];
}
现在,在 模板中使用 Angular 的*ngFor@H_403_13@指令来展示英雄列表的每一项内容。
src/app/app.component.ts (template)
template: ` <h1>{{title}}</h1> <h2>My favorite hero is: {{myHero}}</h2> <p>Heroes:</p> <ul> <li *ngFor="let hero of heroes"> {{ hero }} </li> </ul> `
我们使用了无序列表。在<li>@H_403_13@元素中的
*ngFor@H_403_13@是 Angular 的重复指令。它标记
<li>@H_403_13@元素(以及它的子节点)作为重复的模板。
src/app/app.component.ts (li)
<li *ngFor="let hero of heroes"> {{ hero }} </li>
* 不要忘了ngFor@H_403_13@ 前面的星号
。*@H_403_13@, 它是这个指令语法必不可少的组成部分。 更多的内容,请查阅 模板语法章节 *
注意 在*ngFor@H_403_13@ 指令的双引号之间的
hero@H_403_13@ 是模板的输入变量。更多关于模板输入变量的信息,请查阅模板语法章节的 微语法部分 。
Angular 为列表中的每一项复制了<li>@H_403_13@标签, 把
hero@H_403_13@ 变量设置为当前循环的值。Angular 把这个变量作为 双花括号里插值表达式的上下文。
在这个例子中,ngFor@H_403_13@ 展示了一个数组, 但
ngFor@H_403_13@ 可用于循环任何
可迭代对象@H_403_13@的。
现在,英雄们出现在一个无序列表中:
5.为数据创建类
这个应用的代码直接在组件里定义数据,这不是最好的实现方式。当然,在一个简单的 demo 的,这样写没什么问题。
现在,这里绑定的是一个字符型的数组。在实际的应用中,绑定的大多是特殊的对象。
为了将绑定改为使用特殊的对象,我们把这个英雄名字的数组改写成 Hero@H_403_13@ 对象的数组。 为此,我们需要一个
Hero@H_403_13@ 类。
在这个app@H_403_13@文件夹里创建一个新的文件,命名为
hero.ts@H_403_13@, 代码如下:
src/app/hero.ts
export class Hero {
constructor(
public id: number,public name: string) { }
}
我们定义了一个类,它有一个构造函数和两个属性(id@H_403_13@ 和
name@H_403_13@ )。
它也许看起来不像是一个带有属性的类,但它确实是有属性的。这个构造函数参数的声明使用了 TypeScript@H_403_13@ 简写形式。
想想最初的参数:
src/app/hero.ts (id)
public id: number,
这种简写语法做了许多事情:
5.1 使用 Hero 类
在导入Hero@H_403_13@ 后,
AppComponent.heroes@H_403_13@ 属性返回一个
Hero@H_403_13@ 类型的数组。
src/app/app.component.ts (heroes)
heroes = [
new Hero(1,'Windstorm'),new Hero(13,'Bombasto'),new Hero(15,'Magneta'),new Hero(20,'Tornado')
];
myHero = this.heroes[0];
下一步,更新模板@H_403_13@。现在,模板会展示 hero 的
id@H_403_13@ 和
name@H_403_13@。我们需要修改一下,变成只显示 hero 的
name@H_403_13@属性。
src/app/app.component.ts (template)
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> `.
6.*ngIf@H_403_13@ 有条件地展示数据
有时,应用只在某个特定的条件下,才需要显示视图或者视图的一部分。
让我们来改一下例子:如果超过3个 hero,才显示一条消息。
Angular 的ngIf@H_403_13@ 指令 基于 true 或false 的条件判断来插入或者移除一个元素。
src/app/app.component.ts (message)
<p *ngIf="heroes.length > 3">There are many heroes!</p>
* 不要忘了ngIf@H_403_13@ 前面的星号
。*@H_403_13@, 它是这个指令语法必不可少的组成部分。 更多的内容,请查阅 模板语法章节的ngIf部分 *
在双引号里的模板表达式"heroes.length >3"@H_403_13@ 看起来很像
typescript@H_403_13@。 当组件的 heroes 列表有超过3个 hero 时, Angular 会在 DOM 增加 一个段落,这条消息就会出现。如果小于或等于3个 hero, 那么 Angular 会就忽略这个段落,也就不显示这条消息。更多内容,请查阅模板语法章节的 模板表达式部分 。
Angular 不是显示或者隐藏这条消息。它是从DOM 里新增或者移除了这个<p>@H_403_13@元素。这样做可以提高性能,尤其是在大型项目中需要包含或者排除 大量的有许多数据绑定的 HTML 时。
试一试吧。因为数组里有4个成员,这条消息会显示出来。回到 app.component.ts@H_403_13@ 中,删除或者注释掉 hero 数组中的一个成员。 浏览器会自动刷新,那条消息应该会消失。
7.总结
现在我们知道如何使用:
- 使用双花括号的插值表达式
{{ }}@H_403_13@来展示组件的属性。
*ngFor@H_403_13@ 指令用来展示数组。
- TypeScript 类,用来定义组件的数据类型以及展示这个数据的属性。
*ngIf@H_403_13@ 指令基于表达式的布尔值来决定是否展示一段 HTML 代码。
最终的代码是这样的:
src/app/app.component.ts
import { Component } from '@angular/core';
import { Hero } from './hero';
@Component({
selector: 'my-app',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,'Tornado')
];
myHero = this.heroes[0];
}
src/app/hero.ts
export class Hero {
constructor(
public id: number,public name: string) { }
}
src/app/app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
@NgModule({
imports: [
BrowserModule
],declarations: [
AppComponent
],bootstrap: [ AppComponent ]
})
export class AppModule { }
main.ts
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule);