显示数据

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

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

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

Final UI

目录

Contents

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

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

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

按照开发环境的说明,创建一个新项目,名为displaying-data

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

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

src/app/app.component.ts

  1. import { Component } from '@angular/core';
  2. @Component({
  3. selector: 'my-app',
  4. template: `
  5. <h1>{{title}}</h1>
  6. <h2>My favorite hero is: {{myHero}}</h2>
  7. `
  8. })
  9. export class AppComponent {
  10. title = 'Tour of Heroes';
  11. myHero = 'Windstorm';
  12. }

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

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

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

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

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

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

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

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

src/index.html (body)

<body>
  <my-app>loading...</my-app>
</body>

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

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

Title and Hero

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

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

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

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

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

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

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

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

export class AppCtorComponent {
  title: string;
  myHero: string;

  constructor() {
    this.title = 'Tour of Heroes';
    this.myHero = 'Windstorm';
  }
}

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

使用ngFor显示数组属性

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

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

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

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

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>
  `

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

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

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

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

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

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

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

ngfor之后

为数据创建一个类

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

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

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

app目录下创建一个名叫hero.ts的新文件,内容如下:

src/app/hero.ts (excerpt)

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

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

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

来看第一个参数:

src/app/hero.ts (id)

public id: number,

这个简写语法做了很多:

使用 Hero 类

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

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];

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

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>
`

从显示上看还是一样,但现在我们知道了更多英雄信息。

通过 NgIf 进行条件显示

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

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

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

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

<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,从英雄数组中删除或注释掉一个元素。 浏览器应该自动刷新,消息应该会消失。

小结

现在你知道了如何使用:

下面是最终的代码:

  1. import { Component } from '@angular/core';
  2. import { Hero } from './hero';
  3. @Component({
  4. selector: 'my-app',
  5. template: `
  6. <h1>{{title}}</h1>
  7. <h2>My favorite hero is: {{myHero.name}}</h2>
  8. <p>Heroes:</p>
  9. <ul>
  10. <li *ngFor="let hero of heroes">
  11. {{ hero.name }}
  12. </li>
  13. </ul>
  14. <p *ngIf="heroes.length > 3">There are many heroes!</p>
  15. `
  16. })
  17. export class AppComponent {
  18. title = 'Tour of Heroes';
  19. heroes = [
  20. new Hero(1, 'Windstorm'),
  21. new Hero(13, 'Bombasto'),
  22. new Hero(15, 'Magneta'),
  23. new Hero(20, 'Tornado')
  24. ];
  25. myHero = this.heroes[0];
  26. }

下一步

用户输入