英雄编辑器

为本地开发搭建环境

根据开发环境中的说明创建一个名为的新项目

该项目的文件结构应该是这样的:

angular-tour-of-heroes
src
app
app.component.ts
app.module.ts
main.ts
index.html
styles.css
systemjs.config.js
tsconfig.json
node_modules ...
package.json

在我们完成本章时,得到的应用和这个在线例子 / 可下载的例子一样。

保持应用不断转译和运行

在命令行窗口中输入以下命令:

npm start

这个命令会在“监听”模式下运行TypeScript编译器,当代码变化时,它会自动重新编译。 同时,该命令还会在浏览器中启动该应用,并且当代码变化时刷新浏览器。

在后续构建《英雄指南》过程中,应用能持续运行,而不用中断服务来编译或刷新浏览器。

显示此英雄

AppComponent中添加两个属性:title属性用来表示应用的名字,而hero属性用来表示名叫“Windstorm”的英雄。

app.component.ts (AppComponent class)

export class AppComponent {
  title = 'Tour of Heroes';
  hero = 'Windstorm';
}

下面,更新@Component装饰器中指定的模板,为这些新属性建立数据绑定。

app.component.ts (@Component)

template: `<h1>{{title}}</h1><h2>{{hero}} details!</h2>`

保存后,浏览器应自动刷新,显示标题和英雄。

这里的双大括号是Angular中的插值表达式绑定语法。它们表示组件的titlehero属性的值会作为字符串插入到HTML标签中间。

要了解插值表达式的更多知识,见显示数据

Hero 对象

显然,英雄还需要更多属性。 让我们把hero从一个字符串字面量换成一个类。

创建一个Hero类,它具有idname属性。 现在,把下列代码放在app.component.ts的顶部,仅次于 import 语句。

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

export class Hero {
  id: number;
  name: string;
}

现在,有了一个Hero类,我们把组件hero属性的类型换成Hero。 然后以1为 id、以 “Windstorm” 为名字,初始化它。

src/app/app.component.ts (hero property)

hero: Hero = {
  id: 1,
  name: 'Windstorm'
};

我们把hero从一个字符串换成了对象,所以也得更新模板中的绑定表达式,来引用heroname属性。

template: `<h1>{{title}}</h1><h2>{{hero.name}} details!</h2>`

浏览器自动刷新,并继续显示这位英雄的名字。

使用多行模板字符串添加更多 HTML

要显示英雄的所有属性,还要为英雄的id属性添加一个<div>,为英雄的name属性添加另一个<div>。 为了保持模板的可读性,把每个<div>单独放一行。

反引号包裹的组件模板能让你把<h1><h2><div>元素各自放在一行上。 感谢ES2015和TypeScript的模板字面量特性。要了解更多,请参见模板字面量(Template literals) 页。

app.component.ts (AppComponent's template)

template: `
  <h1>{{title}}</h1>
  <h2>{{hero.name}} details!</h2>
  <div><label>id: </label>{{hero.id}}</div>
  <div><label>name: </label>{{hero.name}}</div>
  `

编辑英雄名字

用户应该能在一个<input>输入框中编辑英雄的名字。 当用户输入时,这个输入框应该能同时显示修改英雄的name属性。

也就是说,我们要在表单元素<input>和组件的hero.name属性之间建立双向绑定。

双向绑定

把模板中的英雄名字重构成这样:

<div>
  <label>name: </label>
  <input [(ngModel)]="hero.name" placeholder="name">
</div>

[(ngModel)]是一个Angular语法,用与把hero.name绑定到输入框中。 它的数据流是双向的:从属性到输入框,并且从输入框回到属性。

不幸的是,做了这项改动之后,我们的程序崩溃了。 打开浏览器的控制台,我们会看到Angular抱怨说:“ngModel ... isn't a known property of input.”(ngModel不是input元素的已知属性)

虽然NgModel是一个有效的Angular指令,但它默认情况下却是不可用的。 它属于一个可选模块FormsModule。 我们必须选择使用那个模块。

导入 FormsModule

打开app.module.ts文件,并且从@angular/forms库中导入符号FormsModule。 然后把FormsModule添加到@NgModule元数据的imports数组中,它是当前应用正在使用的外部模块列表。

修改后的AppModule是这样的:

app.module.ts (FormsModule import)

  1. import { NgModule } from '@angular/core';
  2. import { BrowserModule } from '@angular/platform-browser';
  3. import { FormsModule } from '@angular/forms'; // <-- NgModel lives here
  4. import { AppComponent } from './app.component';
  5. @NgModule({
  6. imports: [
  7. BrowserModule,
  8. FormsModule // <-- import the FormsModule before binding with [(ngModel)]
  9. ],
  10. declarations: [
  11. AppComponent
  12. ],
  13. bootstrap: [ AppComponent ]
  14. })
  15. export class AppModule { }

要学习关于FormsModulengModel的更多知识,参见表单模板语法

浏览器刷新。又见到我们的英雄了。我们可以编辑英雄的名字,也能看到这个改动立刻体现在<h2>中。

我们已经走过的路

我们来盘点一下已经构建完成的部分。

运行这部分的在线例子 / 可下载的例子

完整的app.component.ts是这样的:

src/app/app.component.ts

  1. import { Component } from '@angular/core';
  2. export class Hero {
  3. id: number;
  4. name: string;
  5. }
  6. @Component({
  7. selector: 'my-app',
  8. template: `
  9. <h1>{{title}}</h1>
  10. <h2>{{hero.name}} details!</h2>
  11. <div><label>id: </label>{{hero.id}}</div>
  12. <div>
  13. <label>name: </label>
  14. <input [(ngModel)]="hero.name" placeholder="name">
  15. </div>
  16. `
  17. })
  18. export class AppComponent {
  19. title = 'Tour of Heroes';
  20. hero: Hero = {
  21. id: 1,
  22. name: 'Windstorm'
  23. };
  24. }

前方的路

教程的下一章,我们将在这个《英雄指南》中显示一个英雄列表。 我们将允许允许用户选择一个英雄,并且显示它/她的详情。 我们还将学会如何获取列表以及将它们绑定到模板中。

下一步

主从结构