Merge pull request #164 from todoubaba/toh-pt3

Polish top-pt3.jade (round 2)
This commit is contained in:
Rex 2016-12-01 17:35:55 +00:00 committed by GitHub
commit f6febe222b
1 changed files with 59 additions and 52 deletions

View File

@ -4,8 +4,8 @@ include ../_util-fns
Our app is growing.
Use cases are flowing in for reusing components, passing data to components, and creating more reusable assets. Let's separate the heroes list from the hero details and make the details component reusable.
我们的应用正在成长中。现在又有新的用例:重复使用组件,传递数据给组件并创建更多可复用的资
我们来把英雄详情从英雄列表中分出来,让这个英雄详情组件可以被复用。
我们的应用正在成长中。现在又有新的用例:重复使用组件,传递数据给组件并创建更多可复用的资
我们来把英雄详情从英雄列表中分出来,让这个英雄详情组件可以被复用。
Run the <live-example></live-example> for this part.
@ -19,7 +19,7 @@ include ../_util-fns
Before we continue with our Tour of Heroes, lets verify we have the following structure. If not, well need to go back and follow the previous chapters.
在继续《英雄指南》之前,先检查一下,是否已经有了如下目录结构。如果没有,你得先回上一章,看看错过了哪里。
在继续《英雄指南》之前,先检查一下,是否已经有了如下目录结构。如果没有,回上一章,看看错过了哪里。
.filetree
.file angular-tour-of-heroes
@ -42,7 +42,7 @@ include ../_util-fns
We want to start the TypeScript compiler, have it watch for changes, and start our server. We'll do this by typing
我们要启动TypeScript编译器它会监视文件变更并启动开发服务器。只要敲
我们要启动 TypeScript 编译器,它会监视文件变更,并启动开发服务器。只要敲:
code-example(language="bash").
npm start
@ -63,10 +63,10 @@ code-example(language="bash").
If we had to reuse the hero details elsewhere in our app,
the heroes list would tag along for the ride.
我们的英雄列表和英雄详情目前位于同一个文件的同一个组件中。
现在它们还很小,但很快它们都会长大。
目前,英雄列表和英雄详情位于同一个文件的同一个组件中。
它们现在还很小,但很快它们都会长大。
我们将来肯定会收到新需求:针对这一个,却不能影响另一个。
然而,每一个更改都会给这两个组件带来风险,并且带来双倍的测试负担,却没有任何好处。
然而,每一次更改都会给这两个组件带来风险和双倍的测试负担,却没有任何好处。
如果我们需要在应用的其它地方复用英雄详情组件,英雄列表组件也会跟着混进去。
Our current component violates the
@ -75,7 +75,8 @@ code-example(language="bash").
especially if doing them right is easy and we learn how to build Angular apps in the process.
我们当前的组件违反了[单一职责原则](https://blog.8thlight.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html)。
虽然这只是一个教程,但我们还是得坚持做正确的事 —— 况且做正确的事这么容易我们何乐而不为呢别忘了我们正在学习的就是如何构建真正的Angular应用。
虽然这只是一个教程,但我们还是得坚持做正确的事 &mdash;
况且,做正确的事这么容易,在此过程中,我们又能学习如何构建 Angular 应用。
Lets break the hero details out into its own component.
@ -99,7 +100,7 @@ code-example(language="bash").
We like to identify at a glance which classes are components and which files contain components.
我们希望一眼就能看出哪些类是组件,以及哪些文件包含组件。
我们希望一眼就能看出哪些类是组件,哪些文件包含组件。
Notice that we have an `AppComponent` in a file named `app.component.ts` and our new
`HeroDetailComponent` is in a file named `hero-detail.component.ts`.
@ -114,7 +115,8 @@ code-example(language="bash").
(AKA **[kebab-case](../guide/glossary.html#kebab-case)**) so we don't worry about
case sensitivity on the server or in source control.
这里我们使用小写**[中线命名法](../guide/glossary.html#dash-case)**(也叫**[烤串命名法](../guide/glossary.html#kebab-case)**)拼写文件名,
这里我们使用小写**[中线命名法](../guide/glossary.html#dash-case)**
(也叫**[烤串命名法](../guide/glossary.html#kebab-case)**)拼写文件名,
所以不用担心它在服务器或者版本控制系统中出现大小写问题。
<!-- TODO
@ -126,14 +128,14 @@ code-example(language="bash").
:marked
We begin by importing the `Component` and `Input` decorators from Angular because we're going to need them soon.
一开始,我们先从Angular中导入`Component`和`Input`装饰器,因为马上就会用到它们。
我们先从 Angular 中导入`Component`和`Input`装饰器,因为马上就会用到它们。
We create metadata with the `@Component` decorator where we
specify the selector name that identifies this component's element.
Then we export the class to make it available to other components.
我们使用`@Component`装饰器创建元数据。在元数据中,我们指定选择器的名字,用以标此组件的元素。
然后,我们导出这个组件类,以便其它组件可以使用它。
我们使用`@Component`装饰器创建元数据。在元数据中,我们指定选择器的名字,用以标此组件的元素。
然后,我们导出这个类,以便其它组件可以使用它。
When we finish here, we'll import it into `AppComponent` and create a corresponding `<my-hero-detail>` element.
@ -147,15 +149,15 @@ code-example(language="bash").
At the moment, the *Heroes* and *Hero Detail* views are combined in one template in `AppComponent`.
Lets **cut** the *Hero Detail* content from `AppComponent` and **paste** it into the new template property of `HeroDetailComponent`.
目前,`AppComponent`的*英雄列表*和*英雄详情*视图被组合在同一个模板中。
让我们从`AppComponent`中**剪切**出*英雄详情*的内容,并且粘贴到`HeroDetailComponent`组件的`template`属性中。
此时,`AppComponent`的*英雄列表*和*英雄详情*视图被组合进同一个模板中。
让我们从`AppComponent`中**剪切**出*英雄详情*的内容,并且**粘贴**到`HeroDetailComponent`组件的`template`属性中。
We previously bound to the `selectedHero.name` property of the `AppComponent`.
Our `HeroDetailComponent` will have a `hero` property, not a `selectedHero` property.
So we replace `selectedHero` with `hero` everywhere in our new template. That's our only change.
The result looks like this:
以前我们绑定到了`AppComponent`的`selectedHero.name`属性中
之前我们绑定了`AppComponent`的`selectedHero.name`属性
`HeroDetailComponent`组件将会有一个`hero`属性,而不是`selectedHero`属性。
所以,我们要把模板中的所有`selectedHero`替换为`hero`。只改这些就够了。
最终结果如下所示:
@ -165,7 +167,7 @@ code-example(language="bash").
:marked
Now our hero detail layout exists only in the `HeroDetailComponent`.
现在,我们的英雄详情布局只存在于`HeroDetailComponent`组件中
现在,我们的英雄详情布局只存在于`HeroDetailComponent`组件中。
#### Add the *hero* property
@ -173,7 +175,7 @@ code-example(language="bash").
Lets add that `hero` property we were talking about to the component class.
我们这就添加刚刚所说的`hero`属性到组件类中
把刚刚所说的`hero`属性添加到组件类
+makeExample('toh-3/ts/app/hero-detail.component.ts', 'hero')
@ -181,12 +183,12 @@ code-example(language="bash").
Uh oh. We declared the `hero` property as type `Hero` but our `Hero` class is over in the `app.component.ts` file.
We have two components, each in their own file, that need to reference the `Hero` class.
啊哦!我们定义的`hero`属性是`Hero`类型的,但是我们的`Hero`类还在`app.component.ts`文件中。
我们有了两个组件,它们都有各自的文件,并且都需要引用`Hero`类。
啊哦!我们声明`hero`属性是`Hero`类型,但是我们的`Hero`类还在`app.component.ts`文件中。
我们有了两个组件,它们位于各自的文件,并且都需要引用`Hero`类。
We solve the problem by relocating the `Hero` class from `app.component.ts` to its own `hero.ts` file.
要解决这个问题,我们也得从`app.component.ts`文件中把`Hero`类移到属于它自己的`hero.ts`文件中。
要解决这个问题,我们从`app.component.ts`文件中把`Hero`类移到属于它自己的`hero.ts`文件中。
+makeExample('toh-3/ts/app/hero.ts', '', 'app/hero.ts')(format=".")
@ -194,15 +196,15 @@ code-example(language="bash").
We export the `Hero` class from `hero.ts` because we'll need to reference it in both component files.
Add the following import statement near the top of **both `app.component.ts` and `hero-detail.component.ts`**.
我们从`hero.ts`中导出`Hero`类,因为我们要从那些组件文件中引用它。
在**`app.component.ts`和`hero-detail.component.ts`**的顶部添加下列import语句
我们从`hero.ts`中导出`Hero`类,因为我们要从两个组件文件中引用它。
在**`app.component.ts`和`hero-detail.component.ts`**的顶部添加下列 import 语句:
+makeExample('toh-3/ts/app/hero-detail.component.ts', 'hero-import')
:marked
#### The *hero* property is an ***input***
#### *hero*是一个***输入***属性
#### *hero*属性是一个***输入***属性
The `HeroDetailComponent` must be told what hero to display. Who will tell it? The parent `AppComponent`!
@ -211,13 +213,13 @@ code-example(language="bash").
The `AppComponent` knows which hero to show: the hero that the user selected from the list.
The user's selection is in its `selectedHero` property.
`AppComponent`确实知道该显示哪个英雄 —— 用户从列表中选中的那个。
而这个英雄就是`selectedHero`属性的值
`AppComponent`确实知道该显示哪个英雄用户从列表中选中的那个。
用户选择的英雄在它的`selectedHero`属性中
We will soon update the `AppComponent` template so that it binds its `selectedHero` property
to the `hero` property of our `HeroDetailComponent`. The binding *might* look like this:
我们马上升级`AppComponent`的模板,以便把该组件的`selectedHero`属性绑定到`HeroDetailComponent`组件的`hero`属性上。
我们马上升级`AppComponent`的模板,把该组件的`selectedHero`属性绑定到`HeroDetailComponent`组件的`hero`属性上。
绑定看起来*可能*是这样的:
code-example(format=".").
@ -226,12 +228,12 @@ code-example(format=".").
:marked
Notice that the `hero` property is the ***target*** of a property binding &mdash; it's in square brackets to the left of the (=).
注意,在等号(=)左边方括号中的这个`hero`是属性绑定的***目标***。
注意,`hero`是属性绑定的***目标*** &mdash; 它位于等号 (=) 左边方括号中
Angular insists that we declare a ***target*** property to be an ***input*** property.
If we don't, Angular rejects the binding and throws an error.
Angular希望我们把***目标属性***定义成组件的***输入属性***否则Angular会拒绝绑定并且抛出一个错误。
Angular 希望我们把***目标属性***声明为组件的***输入属性***否则Angular 会拒绝绑定,并抛出错误。
.l-sub-section
:marked
@ -239,14 +241,14 @@ code-example(format=".").
where we also explain why *target* properties require this special treatment and
*source* properties do not.
我们在[这里](../guide/attribute-directives.html#why-input)详细解释了输入属性,以及为什么*目标属性*需要“显式定义”这样的特殊待遇,而*源属性*却不需要。
我们在[这里](../guide/attribute-directives.html#why-input)详细解释了输入属性,以及为什么*目标属性*需要这样的特殊待遇,而*源属性*却不需要。
:marked
There are a couple of ways we can declare that `hero` is an *input*.
We'll do it the way we *prefer*, by annotating the `hero` property with the `@Input` decorator that we imported earlier.
我们有几种方式把`hero`声明成*输入属性*。
这里我们采用*首选*的方式:使用我们前面导入的`@Input`装饰器,为`hero`属性加上注解。
这里我们采用*首选*的方式:使用我们前面导入的`@Input`装饰器向`hero`属性添加注解。
+makeExample('toh-3/ts/app/hero-detail.component.ts', 'hero-input')(format='.')
@ -255,21 +257,21 @@ code-example(format=".").
Learn more about the `@Input()` decorator in the
[Attribute Directives](../guide/attribute-directives.html#input) chapter.
要了解`@Input()`装饰器的更多知识,参见[属性型指令](../guide/attribute-directives.html#input)一章
更多`@Input()`装饰器的信息,见[属性型指令](../guide/attribute-directives.html#input)。
.l-main-section
:marked
## Refresh the AppModule
## 更新AppModule
## 更新 AppModule
We return to the `AppModule`, the application's root module, and teach it to use the `HeroDetailComponent`.
回到`AppModule`,该应用的根模块,我们要教它使用`HeroDetailComponent`组件。
回到应用的根模块`AppModule`,让它使用`HeroDetailComponent`组件。
We begin by importing the `HeroDetailComponent` so we can refer to it.
我们先导入`HeroDetailComponent`组件,好让我们可以引用它。
我们先导入`HeroDetailComponent`组件,后面好引用它。
+makeExample('toh-3/ts/app/app.module.ts', 'hero-detail-import')
@ -278,7 +280,8 @@ code-example(format=".").
This array contains the list of all components, pipes, and directives that we created
and that belong in our application's module.
接下来,添加`HeroDetailComponent`到`NgModule`装饰器中的`declarations`数组。这个数组包含了所有属于本应用模块的,由我们亲自创建的组件、管道和指令。
接下来,添加`HeroDetailComponent`到`NgModule`装饰器中的`declarations`数组。
这个数组包含了所有由我们创建的并属于应用模块的组件、管道和指令。
+makeExample('toh-3/ts/app/app.module.ts', 'declarations')
@ -286,13 +289,15 @@ code-example(format=".").
:marked
## Refresh the AppComponent
## 更新AppComponent
## 更新 AppComponent
:marked
Now that the application knows about our `HeroDetailComponent`,
find the location in the `AppComponent` template where we removed the *Hero Detail* content
and add an element tag that represents the `HeroDetailComponent`.
找到我们刚刚从模板中移除*英雄详情*的地方,放上用来表示`HeroDetailComponent`组件的HTML标签。
现在,应用知道了我们的`HeroDetailComponent`
找到我们刚刚从模板中移除*英雄详情*的地方,
放上用来表示`HeroDetailComponent`组件的元素标签。
code-example(format=".").
&lt;my-hero-detail>&lt;/my-hero-detail>
@ -301,13 +306,14 @@ code-example(format=".").
:marked
*my-hero-detail* is the name we set as the `selector` in the `HeroDetailComponent` metadata.
*my-hero-detail*是我们在`HeroDetailComponent`元数据中的`selector`属性所指定的名字。
*my-hero-detail* 是我们在`HeroDetailComponent`元数据中的`selector`属性所指定的名字。
:marked
The two components won't coordinate until we bind the `selectedHero` property of the `AppComponent`
to the `HeroDetailComponent` element's `hero` property like this:
这两个组件目前还不能协同工作,直到我们把`AppComponent`组件的`selectedHero`属性和`HeroDetailComponent`组件的`hero`属性绑定在一起,就像这样:
这两个组件目前还不能协同工作,直到我们把`AppComponent`组件的`selectedHero`
属性和`HeroDetailComponent`组件的`hero`属性绑定在一起,就像这样:
code-example(format=".")
&lt;my-hero-detail [hero]="selectedHero">&lt;/my-hero-detail>
:marked
@ -320,7 +326,7 @@ code-example(format=".")
Thanks to the binding, the `HeroDetailComponent` should receive the hero from the `AppComponent` and display that hero's detail beneath the list.
The detail should update every time the user picks a new hero.
感谢数据绑定机制,`HeroDetailComponent`组件应该能从`AppComponent`组件中获取英雄数据,并且在列表的下方显示英雄的详情了
感谢数据绑定机制,`HeroDetailComponent`应该能接收来自`AppComponent`的英雄数据,并在列表下方显示英雄的详情
每当用户选中一个新的英雄时,详情信息应该随之更新。
:marked
@ -331,13 +337,13 @@ code-example(format=".")
When we view our app in the browser we see the list of heroes.
When we select a hero we can see the selected heros details.
当在浏览器中查看应用时,我们看到了英雄列表。
当选中一个英雄时,还能看到所选英雄的详情。
当在浏览器中查看应用时,可以看到英雄列表。
当选中一个英雄时,可以看到所选英雄的详情。
What's fundamentally new is that we can use this `HeroDetailComponent`
to show hero details anywhere in the app.
值得关注的进步是:我们可以在应用中的任何地方使用这个`HeroDetailComponent`组件来显示英雄详情
值得关注的进步是:我们可以在应用中的任何地方使用这个`HeroDetailComponent`组件来显示英雄详情。
Weve created our first reusable component!
@ -349,7 +355,7 @@ code-example(format=".")
Lets verify that we have the following structure after all of our good refactoring in this chapter:
来验证下吧,在本章中,经过这些漂亮的重构,我们应该得到下列结构:
来验证下吧,在本章中,经过这些漂亮的重构,我们应该得到下列结构:
.filetree
.file angular-tour-of-heroes
@ -368,7 +374,7 @@ code-example(format=".")
:marked
Here are the code files we discussed in this chapter.
下面就是我们在本章讨论过的源码文件:
下面是我们在本章讨论的代码文件:
+makeTabs(`
toh-3/ts/app/hero-detail.component.ts,
@ -390,24 +396,25 @@ code-example(format=".")
Lets take stock of what weve built.
来盘点一下我们已经构建完的部分
来盘点一下我们已经构建了什么
* We created a reusable component
* 我们创建了一个可复用组件
我们创建了一个可复用组件
* We learned how to make a component accept input
* 我们学会了如何让一个组件接收输入
我们学会了如何让一个组件接收输入
* We learned to declare the application directives we need in an Angular module. We
list the directives in the `NgModule` decorator's `declarations` array.
* 我们学会了在Angular模块中声明该应用所需的指令 —— 只要把这些指令列在`NgModule`装饰器的`declarations`数组中就可以了。
我们学会了在 Angular 模块中声明该应用所需的指令。
只要把这些指令列在`NgModule`装饰器的`declarations`数组中就可以了。
* We learned to bind a parent component to a child component.
* 我们学会了把父组件绑定到子组件。
我们学会了把父组件绑定到子组件。
Run the <live-example></live-example> for this part.
@ -428,7 +435,7 @@ code-example(format=".")
We should refactor data access to a separate service
and share it among the components that need data.
但在`AppComponent`中我们仍然使用着mock数据。
在`AppComponent`中,我们仍然使用着模拟数据。
显然,这种方式不能“可持续发展”。
我们要把数据访问逻辑抽取到一个独立的服务中,并在需要数据的组件之间共享。