# Conflicts: # README.md # public/_includes/_hero-home.jade # public/_includes/_scripts-include.jade # public/docs/dart/latest/_data.json # public/docs/ts/latest/_data.json # public/docs/ts/latest/cookbook/_data.json # public/docs/ts/latest/cookbook/ajs-quick-reference.jade # public/docs/ts/latest/cookbook/aot-compiler.jade # public/docs/ts/latest/cookbook/component-communication.jade # public/docs/ts/latest/cookbook/component-relative-paths.jade # public/docs/ts/latest/cookbook/dependency-injection.jade # public/docs/ts/latest/cookbook/dynamic-form.jade # public/docs/ts/latest/cookbook/form-validation.jade # public/docs/ts/latest/cookbook/i18n.jade # public/docs/ts/latest/cookbook/ngmodule-faq.jade # public/docs/ts/latest/cookbook/set-document-title.jade # public/docs/ts/latest/cookbook/ts-to-js.jade # public/docs/ts/latest/glossary.jade # public/docs/ts/latest/guide/_data.json # public/docs/ts/latest/guide/animations.jade # public/docs/ts/latest/guide/appmodule.jade # public/docs/ts/latest/guide/architecture.jade # public/docs/ts/latest/guide/attribute-directives.jade # public/docs/ts/latest/guide/browser-support.jade # public/docs/ts/latest/guide/change-log.jade # public/docs/ts/latest/guide/component-styles.jade # public/docs/ts/latest/guide/dependency-injection.jade # public/docs/ts/latest/guide/displaying-data.jade # public/docs/ts/latest/guide/forms.jade # public/docs/ts/latest/guide/hierarchical-dependency-injection.jade # public/docs/ts/latest/guide/index.jade # public/docs/ts/latest/guide/lifecycle-hooks.jade # public/docs/ts/latest/guide/ngmodule.jade # public/docs/ts/latest/guide/npm-packages.jade # public/docs/ts/latest/guide/pipes.jade # public/docs/ts/latest/guide/router.jade # public/docs/ts/latest/guide/security.jade # public/docs/ts/latest/guide/server-communication.jade # public/docs/ts/latest/guide/setup-systemjs-anatomy.jade # public/docs/ts/latest/guide/setup.jade # public/docs/ts/latest/guide/structural-directives.jade # public/docs/ts/latest/guide/style-guide.jade # public/docs/ts/latest/guide/template-syntax.jade # public/docs/ts/latest/guide/testing.jade # public/docs/ts/latest/guide/typescript-configuration.jade # public/docs/ts/latest/guide/upgrade.jade # public/docs/ts/latest/guide/user-input.jade # public/docs/ts/latest/guide/webpack.jade # public/docs/ts/latest/index.jade # public/docs/ts/latest/quickstart.jade # public/docs/ts/latest/tutorial/toh-pt1.jade # public/docs/ts/latest/tutorial/toh-pt2.jade # public/docs/ts/latest/tutorial/toh-pt3.jade # public/docs/ts/latest/tutorial/toh-pt4.jade # public/docs/ts/latest/tutorial/toh-pt5.jade # public/docs/ts/latest/tutorial/toh-pt6.jade # public/events.jade # public/presskit.jade # public/resources/js/directives/cheatsheet.js
322 lines
13 KiB
Plaintext
322 lines
13 KiB
Plaintext
include ../_util-fns
|
||
|
||
:marked
|
||
## Setup to develop locally
|
||
|
||
## 为本地开发搭建环境
|
||
|
||
Real application development takes place in a local development environment like your machine.
|
||
|
||
我们通常在本地开发环境中(例如你的机器)进行真实的应用程序开发。
|
||
|
||
Follow the [setup](../guide/setup.html) instructions for creating a new project
|
||
named <ngio-ex path="angular-tour-of-heroes"></ngio-ex>
|
||
after which the file structure should look like this:
|
||
|
||
根据[开发环境](../guide/setup.html)中的说明创建一个名为<ngio-ex path="angular-tour-of-heroes"></ngio-ex>的新项目,
|
||
该项目的文件结构应该是这样的:
|
||
|
||
.filetree
|
||
.file angular-tour-of-heroes
|
||
.children
|
||
.file src
|
||
.children
|
||
.file app
|
||
.children
|
||
.file app.component.ts
|
||
.file app.module.ts
|
||
.file main.ts
|
||
.file index.html
|
||
.file styles.css
|
||
.file systemjs.config.js
|
||
.file tsconfig.json
|
||
.file node_modules ...
|
||
.file package.json
|
||
:marked
|
||
When we're done with this first episode, the app runs like this <live-example></live-example>.
|
||
|
||
在我们完成本章时,得到的应用和这个<live-example>在线例子</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.
|
||
|
||
开发过程中需要启动 TypeScript 编译器,让它监视文件变更,并且启动开发服务器。
|
||
在命令行窗口中输入以下命令:
|
||
|
||
code-example(language="sh" class="code-shell").
|
||
npm start
|
||
|
||
:marked
|
||
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.
|
||
|
||
这个命令会在监视模式下运行编译器,启动开发服务器,在浏览器中启动我们的应用,
|
||
并在后续构建《英雄指南》过程中,应用能持续运行。
|
||
|
||
.l-main-section
|
||
:marked
|
||
## 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".
|
||
|
||
更新`AppComponent`并添加两个属性:`title`属性表示应用的名字,`hero`属性表示一个名叫 “Windstorm” 的英雄。
|
||
|
||
+makeExample('toh-1/ts-snippets/app.component.snippets.pt1.ts', 'app-component-1', 'src/app/app.component.ts (AppComponent class)')(format=".")
|
||
|
||
:marked
|
||
Now update the template in the `@Component` decoration with data bindings to these new properties.
|
||
|
||
下面,更新`@Component`装饰器中指定的模板,为这些新属性建立数据绑定。
|
||
|
||
+makeExample('toh-1/ts-snippets/app.component.snippets.pt1.ts', 'show-hero')
|
||
|
||
:marked
|
||
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.
|
||
|
||
这里的双大括号会告诉应用:从组件中读取`title`和`hero`属性,并且渲染它们。
|
||
这就是单向数据绑定的“插值表达式”形式。
|
||
|
||
.l-sub-section
|
||
:marked
|
||
Learn more about interpolation in the [Displaying Data chapter](../guide/displaying-data.html).
|
||
|
||
要了解插值表达式的更多知识,见[显示数据](../guide/displaying-data.html)。
|
||
|
||
:marked
|
||
### Hero object
|
||
|
||
### Hero 对象
|
||
|
||
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.
|
||
|
||
此时此刻,我们的英雄只是一个名字。显然,它/她应该有更多属性。
|
||
让我们把`hero`从一个字符串字面量换成一个类。
|
||
|
||
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.
|
||
|
||
创建一个`Hero`类,它具有`id`和`name`属性。
|
||
现在,把下列代码放在`app.component.ts`的顶部,仅次于 import 语句。
|
||
|
||
+makeExample('toh-1/ts/src/app/app.component.ts', 'hero-class-1', 'src/app/app.component.ts (Hero class)')(format=".")
|
||
|
||
:marked
|
||
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".
|
||
|
||
现在,有了一个`Hero`类,我们把组件`hero`属性的类型换成`Hero`。
|
||
然后以`1`为 id、以 “Windstorm” 为名字,初始化它。
|
||
|
||
+makeExample('toh-1/ts/src/app/app.component.ts', 'hero-property-1', 'src/app/app.component.ts (hero property)')(format=".")
|
||
|
||
:marked
|
||
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.
|
||
|
||
我们把`hero`从一个字符串换成了对象,所以也得更新模板中的绑定表达式,来引用`hero`的`name`属性。
|
||
|
||
+makeExample('toh-1/ts-snippets/app.component.snippets.pt1.ts', 'show-hero-2')
|
||
:marked
|
||
The browser refreshes and continues to display our hero’s name.
|
||
|
||
浏览器自动刷新,并继续显示这位英雄的名字。
|
||
|
||
### Adding more HTML
|
||
|
||
### 添加更多的 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’s `name`.
|
||
|
||
能显示名字虽然不错,但我们还想看到这位英雄的所有属性。
|
||
我们将添加一个`<div>`来显示英雄的`id`属性,用另一个`<div>`来显示英雄的`name`属性。
|
||
|
||
+makeExample('toh-1/ts-snippets/app.component.snippets.pt1.ts', 'show-hero-properties')
|
||
:marked
|
||
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.
|
||
|
||
我们可以通过字符串加法来制作更可读的模板,但这样仍然太难看了,难于阅读,容易拼错。
|
||
这样不行!我们要借助 ES2015 和 TypeScript 提供的模板字符串来保持清爽。
|
||
|
||
Change the quotes around the template to back-ticks and
|
||
put the `<h1>`, `<h2>` and `<div>` elements on their own lines.
|
||
|
||
把模板的双引号改成反引号,并且让`<h1>`,`<h2>`和`<div>`标签各占一行。
|
||
|
||
+makeExample('toh-1/ts-snippets/app.component.snippets.pt1.ts', 'multi-line-strings', 'src/app/app.component.ts (AppComponent\'s template)')
|
||
|
||
.callout.is-important
|
||
header A back-tick is not a single quote
|
||
|
||
header 反引号不是单引号
|
||
|
||
:marked
|
||
**Be careful!** A back-tick (`) looks a lot like a single quote (').
|
||
It's actually a completely different character.
|
||
Back-ticks can do more than demarcate a string.
|
||
Here we use them in a limited way to spread the template over multiple lines.
|
||
Everything between the back-ticks at the beginning and end of the template
|
||
is part of a single template string.
|
||
|
||
**小心!**反引号 (`) 虽然看起来很像单引号 ('),但它们是截然不同的字符。
|
||
反引号能做的可不仅仅是标记字符串的边界。
|
||
在这里,我们只用它来把我们的模板变成多行的,而没有涉及更多用途。
|
||
所有被反引号引起来的部分,都是一个单一模板字符串的一部分。
|
||
|
||
.l-main-section
|
||
:marked
|
||
## 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:
|
||
|
||
把英雄的名字从单纯的`<label>`重构成`<label>`和`<input>`元素的组合,就像下面这样:
|
||
|
||
+makeExample('toh-1/ts-snippets/app.component.snippets.pt1.ts', 'editing-Hero', 'src/appapp.component.ts (input element)')
|
||
:marked
|
||
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>`.
|
||
|
||
在浏览器中,我们看到英雄的名字显示成一个`<input>`文本框。但看起来还是有点不太对劲。
|
||
当修改名字时,我们的改动并没有反映到`<h2>`中。使用单向数据绑定,我们没法实现所期望的这种行为。
|
||
|
||
### 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.
|
||
|
||
我们的期望是:在`<input>`中显示英雄的名字,修改它,并且在所有绑定到英雄名字的地方看到这些修改。
|
||
简而言之,我们需要双向数据绑定。
|
||
|
||
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 `NgModule` decorator'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`.
|
||
|
||
在我们让**表单输入**支持双向数据绑定之前,我们得先导入`FormsModule`模块。
|
||
只要把它添加到`NgModule`装饰器的`imports`数组中就可以了,该数组是应用中用到的外部模块列表。
|
||
这样我们就引入了表单包,其中包含了`ngModel`。
|
||
|
||
+makeExample('toh-1/ts/src/app/app.module.ts', '', 'src/app/app.module.ts (FormsModule import)')
|
||
|
||
.l-sub-section
|
||
:marked
|
||
Learn more about the `FormsModule` and `ngModel` in the
|
||
[Forms](../guide/forms.html#ngModel) and
|
||
[Template Syntax](../guide/template-syntax.html#ngModel) chapters.
|
||
|
||
要学习关于`FormsModule`和`ngModel`的更多知识,参见[表单](../guide/forms.html#ngModel)和
|
||
[模板语法](../guide/template-syntax.html#ngModel)。
|
||
|
||
:marked
|
||
Let’s update the template to use the **`ngModel`** built-in directive for two-way binding.
|
||
|
||
接下来更新模板,加入用于双向绑定的内置指令**`ngModel`**。
|
||
|
||
Replace the `<input>` with the following HTML
|
||
|
||
把`<input>`替换为下列 HTML
|
||
|
||
code-example(language="html").
|
||
<input [(ngModel)]="hero.name" placeholder="name">
|
||
|
||
:marked
|
||
The browser refreshes. We see our hero again. We can edit the hero’s name and
|
||
see the changes reflected immediately in the `<h2>`.
|
||
|
||
浏览器刷新。又见到我们的英雄了。我们可以编辑英雄的名字,也能看到这个改动立刻体现在`<h2>`中。
|
||
|
||
.l-main-section
|
||
:marked
|
||
## 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 a `Hero` object.
|
||
|
||
我们的《英雄指南》使用双大括号插值表达式(单向数据绑定的一种形式)来显示应用的标题和`Hero`对象的属性。
|
||
|
||
* We wrote a multi-line template using ES2015’s template strings to make our template readable.
|
||
|
||
我们使用 ES2015 的模板字符串写了一个多行模板,使我们的模板更具可读性。
|
||
|
||
* 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.
|
||
|
||
为了同时显示和修改英雄的名字,我们还使用了内置的`ngModel`指令,往`<input>`元素上添加了双向数据绑定。
|
||
|
||
* The `ngModel` directive also propagates changes to every other binding of the `hero.name`.
|
||
|
||
`ngModel`指令将这些修改传播到每一个对`hero.name`的其它绑定。
|
||
|
||
Run the <live-example></live-example> for this part.
|
||
|
||
运行这部分的<live-example>在线例子</live-example>。
|
||
|
||
Here's the complete `app.component.ts` as it stands now:
|
||
|
||
完整的`app.component.ts`是这样的:
|
||
|
||
+makeExample('toh-1/ts/src/app/app.component.ts', 'pt1', 'src/app/app.component.ts')
|
||
|
||
.l-main-section
|
||
:marked
|
||
## 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](./toh-pt2.html).
|
||
|
||
我们的《英雄指南》只显示了一个英雄,而我们真正要显示的是一个英雄列表。
|
||
我们还希望允许用户选择一个英雄,并且显示它/她的详情。
|
||
我们将在[教程的下一章](./toh-pt2.html)中学习如何获取列表数据,并将把它们绑定到模板,以及允许用户选择其中一个英雄。
|