# Master/Detail Components # 主从组件 At the moment, the `HeroesComponent` displays both the list of heroes and the selected hero's details. 此刻,`HeroesComponent` 同时显示了英雄列表和所选英雄的详情。 Keeping all features in one component as the application grows will not be maintainable. You'll want to split up large components into smaller sub-components, each focused on a specific task or workflow. 把所有特性都放在同一个组件中,将会使应用“长大”后变得不可维护。 你要把大型组件拆分成小一点的子组件,每个子组件都要集中精力处理某个特定的任务或工作流。 In this page, you'll take the first step in that direction by moving the hero details into a separate, reusable `HeroDetailComponent`. 本页面中,你将迈出第一步 —— 把英雄详情移入一个独立的、可复用的 `HeroDetailComponent`。 The `HeroesComponent` will only present the list of heroes. The `HeroDetailComponent` will present details of a selected hero. `HeroesComponent` 将仅仅用来表示英雄列表。 `HeroDetailComponent` 将用来表示所选英雄的详情。 ## Make the `HeroDetailComponent` ## 制作 `HeroDetailComponent` Use the Angular CLI to generate a new component named `hero-detail`. 使用 Angular CLI 生成一个名叫 `hero-detail` 的新组件。 ng generate component hero-detail The command scaffolds the `HeroDetailComponent` files and declares the component in `AppModule`. 该命令会生成 `HeroDetailComponent` 文件的脚手架,并把它声明在 `AppModule` 中。 ### Write the template ### 编写模板 Cut the HTML for the hero detail from the bottom of the `HeroesComponent` template and paste it over the generated boilerplate in the `HeroDetailComponent` template. 从 `HeroesComponent` 模板的底部把表示英雄详情的 HTML 代码剪切粘贴到所生成的 `HeroDetailComponent` 模板中。 The pasted HTML refers to a `selectedHero`. The new `HeroDetailComponent` can present _any_ hero, not just a selected hero. So replace "selectedHero" with "hero" everywhere in the template. 所粘贴的 HTML 引用了 `selectedHero`。 新的 `HeroDetailComponent` 可以展示*任意*英雄,而不仅仅所选的。因此还要把模板中的所有 `selectedHero` 替换为 `hero`。 When you're done, the `HeroDetailComponent` template should look like this: 完工之后,`HeroDetailComponent` 的模板应该是这样的: ### Add the `@Input()` hero property ### 添加 `@Input() hero` 属性 The `HeroDetailComponent` template binds to the component's `hero` property which is of type `Hero`. `HeroDetailComponent` 模板中绑定了组件中的 `hero` 属性,它的类型是 `Hero`。 Open the `HeroDetailComponent` class file and import the `Hero` symbol. 打开 `HeroDetailComponent` 类文件,并导入 `Hero` 符号。 The `hero` property [must be an _Input_ property](guide/template-syntax#inputs-outputs "Input and Output properties"), annotated with the `@Input()` decorator, because the _external_ `HeroesComponent` [will bind to it](#heroes-component-template) like this. `hero` 属性[必须是一个带有 `@Input()` 装饰器的输入属性](guide/template-syntax#inputs-outputs "Input and Output properties"),因为*外部的* `HeroesComponent` 组件[将会绑定到它](#heroes-component-template)。就像这样: Amend the `@angular/core` import statement to include the `Input` symbol. 修改 `@angular/core` 的导入语句,导入 `Input` 符号。 Add a `hero` property, preceded by the `@Input()` decorator. 添加一个带有 `@Input()` 装饰器的 `hero` 属性。 That's the only change you should make to the `HeroDetailComponent` class. There are no more properties. There's no presentation logic. This component simply receives a hero object through its `hero` property and displays it. 这就是你要对 `HeroDetailComponent` 类做的唯一一项修改。 没有其它属性,也没有展示逻辑。这个组件所做的只是通过 `hero` 属性接收一个英雄对象,并显示它。 ## Show the `HeroDetailComponent` ## 显示 `HeroDetailComponent` The `HeroesComponent` is still a master/detail view. `HeroesComponent` 仍然是主从视图。 It used to display the hero details on its own, before you cut that portion of the template. Now it will delegate to the `HeroDetailComponent`. 在你从模板中剪切走代码之前,它自己负责显示英雄的详情。现在它要把这个职责委托给 `HeroDetailComponent` 了。 The two components will have a parent/child relationship. The parent `HeroesComponent` will control the child `HeroDetailComponent` by sending it a new hero to display whenever the user selects a hero from the list. 这两个组件将会具有父子关系。 当用户从列表中选择了某个英雄时,父组件 `HeroesComponent` 将通过把要显示的新英雄发送给子组件 `HeroDetailComponent`,来控制子组件。 You won't change the `HeroesComponent` _class_ but you will change its _template_. 你不用修改 `HeroesComponent` *类*,但是要修改它的*模板*。 {@a heroes-component-template} ### Update the `HeroesComponent` template ### 修改 `HeroesComponent` 的模板 The `HeroDetailComponent` selector is `'app-hero-detail'`. Add an `` element near the bottom of the `HeroesComponent` template, where the hero detail view used to be. `HeroDetailComponent` 的选择器是 `'app-hero-detail'`。 把 `` 添加到 `HeroesComponent` 模板的底部,以便把英雄详情的视图显示到那里。 Bind the `HeroesComponent.selectedHero` to the element's `hero` property like this. 把 `HeroesComponent.selectedHero` 绑定到钙元素的 `hero` 属性,就像这样: `[hero]="selectedHero"` is an Angular [property binding](guide/template-syntax#property-binding). `[hero]="selectedHero"` 是 Angular 的[属性绑定](guide/template-syntax#property-binding)语法。 It's a _one way_ data binding from the `selectedHero` property of the `HeroesComponent` to the `hero` property of the target element, which maps to the `hero` property of the `HeroDetailComponent`. 这是一种*单向*数据绑定。从 `HeroesComponent` 的 `selectedHero` 属性绑定到目标元素的 `hero` 属性,并映射到了 `HeroDetailComponent` 的 `hero` 属性。 Now when the user clicks a hero in the list, the `selectedHero` changes. When the `selectedHero` changes, the _property binding_ updates `hero` and the `HeroDetailComponent` displays the new hero. 现在,当用户在列表中点击某个英雄时,`selectedHero` 就改变了。 当 `selectedHero` 改变时,*属性绑定*会修改 `HeroDetailComponent` 的 `hero` 属性,`HeroDetailComponent` 就会显示这个新的英雄。 The revised `HeroesComponent` template should look like this: 修改后的 `HeroesComponent` 的模板是这样的: The browser refreshes and the app starts working again as it did before. 浏览器刷新,应用又像以前一样开始工作了。 ## What changed? ## 有哪些变化? As [before](tutorial/toh-pt2), whenever a user clicks on a hero name, the hero detail appears below the hero list. Now the `HeroDetailComponent` is presenting those details instead of the `HeroesComponent`. 像[以前](tutorial/toh-pt2)一样,一旦用户点击了一个英雄的名字,该英雄的详情就显示在了英雄列表下方。 现在,`HeroDetailComponent` 负责显示那些详情,而不再是 `HeroesComponent`。 Refactoring the original `HeroesComponent` into two components yields benefits, both now and in the future: 把原来的 `HeroesComponent` 重构成两个组件带来了一些优点,无论是现在还是未来: 1. You simplified the `HeroesComponent` by reducing its responsibilities. 你通过缩减 `HeroesComponent` 的职责简化了该组件。 1. You can evolve the `HeroDetailComponent` into a rich hero editor without touching the parent `HeroesComponent`. 你可以把 `HeroDetailComponent` 改进成一个功能丰富的英雄编辑器,而不用改动父组件 `HeroesComponent`。 1. You can evolve the `HeroesComponent` without touching the hero detail view. 你可以改进 `HeroesComponent`,而不用改动英雄详情视图。 1. You can re-use the `HeroDetailComponent` in the template of some future component. 将来你可以在其它组件的模板中重复使用 `HeroDetailComponent`。 ## Final code review ## 查看最终代码 Here are the code files discussed on this page and your app should look like this . 你的应用应该变成了这样 。本页所提及的代码文件如下: ## Summary ## 小结 * You created a separate, reusable `HeroDetailComponent`. 你创建了一个独立的、可复用的 `HeroDetailComponent` 组件。 * You used a [property binding](guide/template-syntax#property-binding) to give the parent `HeroesComponent` control over the child `HeroDetailComponent`. 你用[属性绑定](guide/template-syntax#property-binding)语法来让父组件 `HeroesComponent` 可以控制子组件 `HeroDetailComponent`。 * You used the [`@Input` decorator](guide/template-syntax#inputs-outputs) to make the `hero` property available for binding by the external `HeroesComponent`. 你用 [`@Input` 装饰器](guide/template-syntax#inputs-outputs)来让 `hero` 属性可以在外部的 `HeroesComponent` 中绑定。