AngularJS教程:英雄编辑器

英雄编辑器

本地搭建环境去开发

真正的应用程序开发是发生在像你的机器一样的本地的开发环境中的。

跟随着 setup (这里是源地址,英文版的,读者也可以参考我的帖子AngularJS学习之二:配置本地开发环境指导去创建一个新的名为angular-tour-of-heroes 的项目后,文件结构看起来就像这样:

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

当我们完成了这第一章节,这个app运行起来就像 live example.

保持app处于转译和运行中
(注:这里转译的原词是transpiling,这个词在百度、有道、科林斯都查不到,谷歌解释为转译,我理解为自动编译和加载)

我们想要启动TypeScript编译器,让它来监控变化,并且启动服务。通过在终端窗口输入下面的指令来完成这个。

npm start

这个命令以观察模式运行编译器,启动服务,在浏览器运行app,当我们继续构建英雄之旅的时候保持着app的运行。

展示我们的英雄

我们想要在我们的app中显示英雄的数据。

更新这个 AppComponent ,这样它就有两个属性:  a title 属性显示应用的名字和一个hero 属性显示一个名字叫"Windstorm"的英雄。

app.component.ts (AppComponent class)

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

现在使用对这些属性的数据绑定来更新在 @Component 装饰里面的模板。

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

浏览器应该刷新并且显示我们的标题和英雄。

这双重的花括号告诉我们的app去从组件中读取 title 和hero 属性,并且去渲染它们。这就是单向数据绑定的“插值”模式。

在 Displaying Data chapter 去学习更多的插值内容。

英雄对象

在这个时候,我们的英雄只是一个名字。我们的英雄需要更多的属性。让我们把 hero 从一个字符串转换成一个类吧。


使用id 和 name 属性来创建 Hero 类。现在把它放在app.component.ts 文件顶部附近,仅仅在import语句下面。

app.component.ts (Hero class)

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

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

app.component.ts (hero property)

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

因为我们把这个英雄从一个字符串转换成了一个对象,我们更新在模板中指向 name 属性的绑定。

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

浏览器刷新并且继续显示我们的英雄的名字。

增加更多的HTML

显示一个名字是好的,但是我们还想要看到我们的英雄的所有的属性。我们增加一个 <div> 给我们英雄的 id 属性和 另外一个 <div> 给我们英雄的name属性。

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

哦,我们的模板字符串在变长。我们最好小心一些,避免在模板中出现输入错误的风险。

多行模板字符串

我们可以使用字符串连接来制作一个更可读的模板,但是它很快会变得难看,不容易去阅读,容易制造拼写错误。取而代之的,让我们来充分利用在ES2015 和TypeScript中的模板字符串功能来保持我们的清爽。

修改围绕模板的引号为反引号,然后把这些 <h1><h2> 和<div> 元素各放到一行上。

app.component.ts (AppComponent's template)

  1. template:`
  2. <h1>{{title}}</h1>
  3. <h2>{{hero.name}} details!</h2>
  4. <div><label>id: </label>{{hero.id}}</div>
  5. <div><label>name: </label>{{hero.name}}</div>
  6. `

修改我们的英雄

我们希望能够在一个文本编辑英雄的名字。

像下面这样用<label> 和<input>反射这个英雄的名字的 <label> :

app.component.ts (input element)

  1. template:`
  2. <h1>{{title}}</h1>
  3. <h2>{{hero.name}} details!</h2>
  4. <div><label>id: </label>{{hero.id}}</div>
  5. <div>
  6. <label>name: </label>
  7. <input value="{{hero.name}}" placeholder="name">
  8. </div>
  9. `

我们在浏览器看到这个英雄的名字显示在<input>文本框中。但是有些事感觉不对。当我们修改名字的时候,我们发现我们的修改没有影响到<h2>。我们使用到<input>单向绑定将不会得到想要的结果。

双向绑定

我们尝试在<input>显示英雄的名字,修改它,并且看到这些变化,无论我们在哪里绑定了英雄的名字。简而言之,我们想要双向绑定。

在我们可以使用对于 form inputs 的双向绑定之前,我们需要在我们的Angular模块中导入FormsModule包。我们把它加入到 NgModule装饰器的 imports 数组。这个数组包含被你的应用所使用的外部模块列表。现在我们已经引入了表单的包,这个包里面包含 ngModel

app.module.ts (FormsModule import)

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

在 Forms 和 Template Syntax 章节可以了解更多关于 FormsModule and ngModel 的信息。

让我们使用内建的指示符ngModel 来更新模板,从而达到双向绑定的作用。

使用以下的<input [(ngModel)]="hero.name"placeholder="name">来取代原本的<input> 。

浏览器刷新,我们又看到了我们的英雄。我们可以修改这个英雄的名字,并且立刻在 <h2>看到反馈出来的效果。

我们已经走过的路

让我们清点一下我们已经做的事情。

  • 我们的英雄之旅使用了插值的双花括号(一种单向数据绑定)来显示一个应用程序的标题和一个 Hero 对象的属性。
  • 我们使用ES2015的模板字符串编写了多行模板去让我们的模板具有可读性。
  • 我们使用内建的ngModel 指示符来增加对<input> 元素的双向绑定后,可以达到显示和修改英雄名字的作用。
  • 这个ngModel 指示符还可以传播hero.name 的任何其它绑定的修改。

运行这部分的 live example 。

这是完整的 app.component.ts ,它现在这个样子:

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. }

前方的路

我们的英雄之旅仅仅显示了一个英雄,我们其实想要显示一个英雄列表。我们同时还想要允许用户去选择一个英雄,并且显示他的细节。我们将在 next tutorial chapter (下一个教程章节)学到更多关于如何去检索列表,把它们绑定到模板,允许用户去选择一个英雄。

以下是原文,摘自:https://angular.io/docs/ts/latest/tutorial/toh-pt1.html。

Setup to develop locally

Real application development takes place in a local development environment like your machine.

Follow the setup instructions for creating a new project named angular-tour-of-heroes after which the file structure should look like this:

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

When we're done with this first episode, the app runs like this live example.

Keep the app transpiling and running

We want to start the TypeScript compiler, have it watch for changes, and start our server. Do this by entering the following command in the terminal window.

npm start

This command runs the compiler in watch mode, starts the server, launches the app in a browser, and keeps the app running while we continue to build the Tour of Heroes.

Show our Hero

We want to display Hero data in our app

Update the AppComponent so it has two properties:   a title property for the application name and a hero property for a hero named "Windstorm".

app.component.ts (AppComponent class)

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

Now update the template in the @Component decoration with data bindings to these new properties.

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

The browser should refresh and display our title and hero.

The double curly braces tell our app to read the title and hero properties from the component and render them. This is the "interpolation" form of one-way data binding.

Learn more about interpolation in the Displaying Data chapter.

Hero object

At the moment, our hero is just a name. Our hero needs more properties. Let's convert the hero from a literal string to a class.

Create a Hero class with id and name properties. For now put this near the top of the app.component.ts file, just below the import statement.

app.component.ts (Hero class)

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

Now that we have a Hero class, let’s refactor our component’s hero property to be of type Hero. Then initialize it with an id of 1 and the name, "Windstorm".

app.component.ts (hero property)

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

Because we changed the hero from a string to an object, we update the binding in the template to refer to the hero’s name property.

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

The browser refreshes and continues to display our hero’s name.

Adding more HTML

Displaying a name is good, but we want to see all of our hero’s properties. We’ll add a <div> for our hero’s id property and another <div> for our hero’sname.

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

Uh oh, our template string is getting long. We better take care of that to avoid the risk of making a typo in the template.

Multi-line template strings

We could make a more readable template with string concatenation but that gets ugly fast, it is harder to read, and it is easy to make a spelling error. Instead, let’s take advantage of the template strings feature in ES2015 and TypeScript to maintain our sanity.

Change the quotes around the template to back-ticks and put the <h1><h2> and <div> elements on their own lines.

app.component.ts (AppComponent's template)

  1. template:`
  2. <h1>{{title}}</h1>
  3. <h2>{{hero.name}} details!</h2>
  4. <div><label>id: </label>{{hero.id}}</div>
  5. <div><label>name: </label>{{hero.name}}</div>
  6. `

Editing Our Hero

We want to be able to edit the hero name in a textbox.

Refactor the hero name <label> with <label> and <input> elements as shown below:

app.component.ts (input element)

  1. template:`
  2. <h1>{{title}}</h1>
  3. <h2>{{hero.name}} details!</h2>
  4. <div><label>id: </label>{{hero.id}}</div>
  5. <div>
  6. <label>name: </label>
  7. <input value="{{hero.name}}" placeholder="name">
  8. </div>
  9. `

We see in the browser that the hero’s name does appear in the <input> textbox. But something doesn’t feel right. When we change the name, we notice that our change is not reflected in the <h2>. We won't get the desired behavior with a one-way binding to <input>.

Two-Way Binding

We intend to display the name of the hero in the <input>, change it, and see those changes wherever we bind to the hero’s name. In short, we want two-way data binding.

Before we can use two-way data binding for form inputs, we need to import the FormsModule package in our Angular module. We add it to the NgModuledecorator's imports array. This array contains the list of external modules used by our application. Now we have included the forms package which includes ngModel.

app.module.ts (FormsModule import)

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

Learn more about the FormsModule and ngModel in the Forms and Template Syntax chapters.

Let’s update the template to use the ngModel built-in directive for two-way binding.

Replace the <input> with the following HTML

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

The browser refreshes. We see our hero again. We can edit the hero’s name and see the changes reflected immediately in the <h2>.

The Road We’ve Travelled

Let’s take stock of what we’ve built.

  • Our Tour of Heroes uses the double curly braces of interpolation (a kind of one-way data binding) to display the application title and properties of aHero object.
  • We wrote a multi-line template using ES2015’s template strings to make our template readable.
  • We can both display and change the hero’s name after adding a two-way data binding to the <input> element using the built-in ngModel directive.
  • The ngModel directive also propagates changes to every other binding of the hero.name.

Run the live example for this part.

Here's the complete app.component.ts as it stands now:

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. }

The Road Ahead

Our Tour of Heroes only displays one hero and we really want to display a list of heroes. We also want to allow the user to select a hero and display their details. We’ll learn more about how to retrieve lists, bind them to the template, and allow a user to select a hero in the next tutorial chapter.



posted on 2017-01-08 10:46  chaiyu2002  阅读(106)  评论(0编辑  收藏  举报

导航