5 lines
16 KiB
JSON

{
"id": "tutorial/toh-pt3",
"title": "Create a feature component",
"contents": "\n\n\n<div class=\"github-links\">\n <a href=\"https://github.com/angular/angular/edit/master/aio/content/tutorial/toh-pt3.md?message=docs%3A%20describe%20your%20change...\" aria-label=\"Suggest Edits\" title=\"Suggest Edits\"><i class=\"material-icons\" aria-hidden=\"true\" role=\"img\">mode_edit</i></a>\n</div>\n\n\n<div class=\"content\">\n <h1 id=\"create-a-feature-component\">Create a feature component<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"tutorial/toh-pt3#create-a-feature-component\"><i class=\"material-icons\">link</i></a></h1>\n<p>At the moment, the <code>HeroesComponent</code> displays both the list of heroes and the selected hero's details.</p>\n<p>Keeping all features in one component as the application grows will not be maintainable.\nYou'll want to split up large components into smaller sub-components, each focused on a specific task or workflow.</p>\n<p>In this page, you'll take the first step in that direction by moving the hero details into a separate, reusable <code>HeroDetailComponent</code>.</p>\n<p>The <code>HeroesComponent</code> will only present the list of heroes.\nThe <code>HeroDetailComponent</code> will present details of a selected hero.</p>\n<div class=\"alert is-helpful\">\n<p> For the sample application that this page describes, see the <live-example></live-example>.</p>\n</div>\n<h2 id=\"make-the-herodetailcomponent\">Make the <code>HeroDetailComponent</code><a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"tutorial/toh-pt3#make-the-herodetailcomponent\"><i class=\"material-icons\">link</i></a></h2>\n<p>Use the Angular CLI to generate a new component named <code>hero-detail</code>.</p>\n<code-example language=\"sh\" class=\"code-shell\">\n ng generate component hero-detail\n</code-example>\n<p>The command scaffolds the following:</p>\n<ul>\n<li>Creates a directory <code>src/app/hero-detail</code>.</li>\n</ul>\n<p>Inside that directory four files are generated:</p>\n<ul>\n<li>A CSS file for the component styles.</li>\n<li>An HTML file for the component template.</li>\n<li>A TypeScript file with a component class named <code>HeroDetailComponent</code>.</li>\n<li>A test file for the <code>HeroDetailComponent</code> class.</li>\n</ul>\n<p>The command also adds the <code>HeroDetailComponent</code> as a declaration in the <code>@<a href=\"api/core/NgModule\" class=\"code-anchor\">NgModule</a></code> decorator of the <code>src/app/app.module.ts</code> file.</p>\n<h3 id=\"write-the-template\">Write the template<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"tutorial/toh-pt3#write-the-template\"><i class=\"material-icons\">link</i></a></h3>\n<p>Cut the HTML for the hero detail from the bottom of the <code>HeroesComponent</code> template and paste it over the generated boilerplate in the <code>HeroDetailComponent</code> template.</p>\n<p>The pasted HTML refers to a <code>selectedHero</code>.\nThe new <code>HeroDetailComponent</code> can present <em>any</em> hero, not just a selected hero.\nSo replace \"selectedHero\" with \"hero\" everywhere in the template.</p>\n<p>When you're done, the <code>HeroDetailComponent</code> template should look like this:</p>\n<code-example path=\"toh-pt3/src/app/hero-detail/hero-detail.component.html\" header=\"src/app/hero-detail/hero-detail.component.html\">\n&#x3C;div *<a href=\"api/common/NgIf\" class=\"code-anchor\">ngIf</a>=\"hero\">\n\n &#x3C;h2>{{hero.name | <a href=\"api/common/UpperCasePipe\" class=\"code-anchor\">uppercase</a>}} Details&#x3C;/h2>\n &#x3C;div>&#x3C;span>id: &#x3C;/span>{{hero.id}}&#x3C;/div>\n &#x3C;div>\n &#x3C;label for=\"hero-name\">Hero name: &#x3C;/label>\n &#x3C;input id=\"hero-name\" [(<a href=\"api/forms/NgModel\" class=\"code-anchor\">ngModel</a>)]=\"hero.name\" placeholder=\"name\">\n &#x3C;/div>\n\n&#x3C;/div>\n\n\n</code-example>\n<h3 id=\"add-the-input-hero-property\">Add the <code>@<a href=\"api/core/Input\" class=\"code-anchor\">Input</a>()</code> hero property<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"tutorial/toh-pt3#add-the-input-hero-property\"><i class=\"material-icons\">link</i></a></h3>\n<p>The <code>HeroDetailComponent</code> template binds to the component's <code>hero</code> property\nwhich is of type <code>Hero</code>.</p>\n<p>Open the <code>HeroDetailComponent</code> class file and import the <code>Hero</code> symbol.</p>\n<code-example path=\"toh-pt3/src/app/hero-detail/hero-detail.component.ts\" region=\"import-hero\" header=\"src/app/hero-detail/hero-detail.component.ts (import Hero)\">\nimport { Hero } from '../hero';\n\n</code-example>\n<p>The <code>hero</code> property\n<a href=\"guide/inputs-outputs\" title=\"Input and Output properties\">must be an <em>Input</em> property</a>,\nannotated with the <code>@<a href=\"api/core/Input\" class=\"code-anchor\">Input</a>()</code> decorator,\nbecause the <em>external</em> <code>HeroesComponent</code> <a href=\"tutorial/toh-pt3#heroes-component-template\">will bind to it</a> like this.</p>\n<code-example path=\"toh-pt3/src/app/heroes/heroes.component.html\" region=\"hero-detail-binding\">\n&#x3C;app-hero-detail [hero]=\"selectedHero\">&#x3C;/app-hero-detail>\n\n</code-example>\n<p>Amend the <code>@angular/core</code> import statement to include the <code><a href=\"api/core/Input\" class=\"code-anchor\">Input</a></code> symbol.</p>\n<code-example path=\"toh-pt3/src/app/hero-detail/hero-detail.component.ts\" region=\"import-input\" header=\"src/app/hero-detail/hero-detail.component.ts (import Input)\">\nimport { <a href=\"api/core/Component\" class=\"code-anchor\">Component</a>, <a href=\"api/core/OnInit\" class=\"code-anchor\">OnInit</a>, <a href=\"api/core/Input\" class=\"code-anchor\">Input</a> } from '@angular/core';\n\n</code-example>\n<p>Add a <code>hero</code> property, preceded by the <code>@<a href=\"api/core/Input\" class=\"code-anchor\">Input</a>()</code> decorator.</p>\n<code-example path=\"toh-pt3/src/app/hero-detail/hero-detail.component.ts\" header=\"src/app/hero-detail/hero-detail.component.ts\" region=\"input-hero\">\n@<a href=\"api/core/Input\" class=\"code-anchor\">Input</a>() hero?: Hero;\n\n</code-example>\n<p>That's the only change you should make to the <code>HeroDetailComponent</code> class.\nThere are no more properties. There's no presentation logic.\nThis component only receives a hero object through its <code>hero</code> property and displays it.</p>\n<h2 id=\"show-the-herodetailcomponent\">Show the <code>HeroDetailComponent</code><a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"tutorial/toh-pt3#show-the-herodetailcomponent\"><i class=\"material-icons\">link</i></a></h2>\n<p>The <code>HeroesComponent</code> used to display the hero details on its own, before you removed that portion of the template.\nThis section guides you through delegating logic to the <code>HeroDetailComponent</code>.</p>\n<p>The two components will have a parent/child relationship.\nThe parent <code>HeroesComponent</code> will control the child <code>HeroDetailComponent</code>\nby sending it a new hero to display whenever\nthe user selects a hero from the list.</p>\n<p>You won't change the <code>HeroesComponent</code> <em>class</em> but you will change its <em>template</em>.</p>\n<a id=\"heroes-component-template\"></a>\n<h3 id=\"update-the-heroescomponent-template\">Update the <code>HeroesComponent</code> template<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"tutorial/toh-pt3#update-the-heroescomponent-template\"><i class=\"material-icons\">link</i></a></h3>\n<p>The <code>HeroDetailComponent</code> selector is <code>'app-hero-detail'</code>.\nAdd an <code>&#x3C;app-hero-detail></code> element near the bottom of the <code>HeroesComponent</code> template, where the hero detail view used to be.</p>\n<p>Bind the <code>HeroesComponent.selectedHero</code> to the element's <code>hero</code> property like this.</p>\n<code-example path=\"toh-pt3/src/app/heroes/heroes.component.html\" region=\"hero-detail-binding\" header=\"heroes.component.html (HeroDetail binding)\">\n&#x3C;app-hero-detail [hero]=\"selectedHero\">&#x3C;/app-hero-detail>\n\n</code-example>\n<p><code>[hero]=\"selectedHero\"</code> is an Angular <a href=\"guide/property-binding\">property binding</a>.</p>\n<p>It's a <em>one way</em> data binding from\nthe <code>selectedHero</code> property of the <code>HeroesComponent</code> to the <code>hero</code> property of the target element, which maps to the <code>hero</code> property of the <code>HeroDetailComponent</code>.</p>\n<p>Now when the user clicks a hero in the list, the <code>selectedHero</code> changes.\nWhen the <code>selectedHero</code> changes, the <em>property binding</em> updates <code>hero</code>\nand the <code>HeroDetailComponent</code> displays the new hero.</p>\n<p>The revised <code>HeroesComponent</code> template should look like this:</p>\n<code-example path=\"toh-pt3/src/app/heroes/heroes.component.html\" header=\"heroes.component.html\">\n&#x3C;h2>My Heroes&#x3C;/h2>\n\n&#x3C;ul class=\"heroes\">\n &#x3C;li *<a href=\"api/common/NgForOf\" class=\"code-anchor\">ngFor</a>=\"let hero of heroes\"\n [class.selected]=\"hero === selectedHero\"\n (click)=\"onSelect(hero)\">\n &#x3C;span class=\"badge\">{{hero.id}}&#x3C;/span> {{hero.name}}\n &#x3C;/li>\n&#x3C;/ul>\n\n&#x3C;app-hero-detail [hero]=\"selectedHero\">&#x3C;/app-hero-detail>\n\n\n</code-example>\n<p>The browser refreshes and the application starts working again as it did before.</p>\n<h2 id=\"what-changed\">What changed?<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"tutorial/toh-pt3#what-changed\"><i class=\"material-icons\">link</i></a></h2>\n<p>As <a href=\"tutorial/toh-pt2\">before</a>, whenever a user clicks on a hero name,\nthe hero detail appears below the hero list.\nNow the <code>HeroDetailComponent</code> is presenting those details instead of the <code>HeroesComponent</code>.</p>\n<p>Refactoring the original <code>HeroesComponent</code> into two components yields benefits, both now and in the future:</p>\n<ol>\n<li>\n<p>You reduced the <code>HeroesComponent</code> responsibilities.</p>\n</li>\n<li>\n<p>You can evolve the <code>HeroDetailComponent</code> into a rich hero editor\nwithout touching the parent <code>HeroesComponent</code>.</p>\n</li>\n<li>\n<p>You can evolve the <code>HeroesComponent</code> without touching the hero detail view.</p>\n</li>\n<li>\n<p>You can re-use the <code>HeroDetailComponent</code> in the template of some future component.</p>\n</li>\n</ol>\n<h2 id=\"final-code-review\">Final code review<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"tutorial/toh-pt3#final-code-review\"><i class=\"material-icons\">link</i></a></h2>\n<p>Here are the code files discussed on this page.</p>\n<code-tabs>\n\n <code-pane header=\"src/app/hero-detail/hero-detail.component.ts\" path=\"toh-pt3/src/app/hero-detail/hero-detail.component.ts\">\nimport { <a href=\"api/core/Component\" class=\"code-anchor\">Component</a>, <a href=\"api/core/OnInit\" class=\"code-anchor\">OnInit</a>, <a href=\"api/core/Input\" class=\"code-anchor\">Input</a> } from '@angular/core';\nimport { Hero } from '../hero';\n\n@<a href=\"api/core/Component\" class=\"code-anchor\">Component</a>({\n selector: 'app-hero-detail',\n templateUrl: './hero-detail.component.html',\n styleUrls: ['./hero-detail.component.css']\n})\nexport class HeroDetailComponent implements <a href=\"api/core/OnInit\" class=\"code-anchor\">OnInit</a> {\n @<a href=\"api/core/Input\" class=\"code-anchor\">Input</a>() hero?: Hero;\n\n constructor() { }\n\n ngOnInit() {\n }\n\n}\n\n\n</code-pane>\n\n <code-pane header=\"src/app/hero-detail/hero-detail.component.html\" path=\"toh-pt3/src/app/hero-detail/hero-detail.component.html\">\n&#x3C;div *<a href=\"api/common/NgIf\" class=\"code-anchor\">ngIf</a>=\"hero\">\n\n &#x3C;h2>{{hero.name | <a href=\"api/common/UpperCasePipe\" class=\"code-anchor\">uppercase</a>}} Details&#x3C;/h2>\n &#x3C;div>&#x3C;span>id: &#x3C;/span>{{hero.id}}&#x3C;/div>\n &#x3C;div>\n &#x3C;label for=\"hero-name\">Hero name: &#x3C;/label>\n &#x3C;input id=\"hero-name\" [(<a href=\"api/forms/NgModel\" class=\"code-anchor\">ngModel</a>)]=\"hero.name\" placeholder=\"name\">\n &#x3C;/div>\n\n&#x3C;/div>\n\n\n</code-pane>\n\n <code-pane header=\"src/app/heroes/heroes.component.html\" path=\"toh-pt3/src/app/heroes/heroes.component.html\">\n&#x3C;h2>My Heroes&#x3C;/h2>\n\n&#x3C;ul class=\"heroes\">\n &#x3C;li *<a href=\"api/common/NgForOf\" class=\"code-anchor\">ngFor</a>=\"let hero of heroes\"\n [class.selected]=\"hero === selectedHero\"\n (click)=\"onSelect(hero)\">\n &#x3C;span class=\"badge\">{{hero.id}}&#x3C;/span> {{hero.name}}\n &#x3C;/li>\n&#x3C;/ul>\n\n&#x3C;app-hero-detail [hero]=\"selectedHero\">&#x3C;/app-hero-detail>\n\n\n</code-pane>\n\n <code-pane header=\"src/app/app.module.ts\" path=\"toh-pt3/src/app/app.module.ts\">\nimport { <a href=\"api/platform-browser/BrowserModule\" class=\"code-anchor\">BrowserModule</a> } from '@angular/platform-browser';\nimport { <a href=\"api/core/NgModule\" class=\"code-anchor\">NgModule</a> } from '@angular/core';\nimport { <a href=\"api/forms/FormsModule\" class=\"code-anchor\">FormsModule</a> } from '@angular/forms';\n\nimport { AppComponent } from './app.component';\nimport { HeroesComponent } from './heroes/heroes.component';\nimport { HeroDetailComponent } from './hero-detail/hero-detail.component';\n\n@<a href=\"api/core/NgModule\" class=\"code-anchor\">NgModule</a>({\n declarations: [\n AppComponent,\n HeroesComponent,\n HeroDetailComponent\n ],\n imports: [\n <a href=\"api/platform-browser/BrowserModule\" class=\"code-anchor\">BrowserModule</a>,\n <a href=\"api/forms/FormsModule\" class=\"code-anchor\">FormsModule</a>\n ],\n providers: [],\n bootstrap: [AppComponent]\n})\nexport class AppModule { }\n\n\n</code-pane>\n\n</code-tabs>\n<h2 id=\"summary\">Summary<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"tutorial/toh-pt3#summary\"><i class=\"material-icons\">link</i></a></h2>\n<ul>\n<li>You created a separate, reusable <code>HeroDetailComponent</code>.</li>\n</ul>\n<ul>\n<li>You used a <a href=\"guide/property-binding\">property binding</a> to give the parent <code>HeroesComponent</code> control over the child <code>HeroDetailComponent</code>.</li>\n</ul>\n<ul>\n<li>You used the <a href=\"guide/inputs-outputs\"><code>@Input</code> decorator</a>\nto make the <code>hero</code> property available for binding\nby the external <code>HeroesComponent</code>.</li>\n</ul>\n\n \n</div>\n\n<!-- links to this doc:\n - guide/example-apps-list\n-->\n<!-- links from this doc:\n - api/common/NgForOf\n - api/common/NgIf\n - api/common/UpperCasePipe\n - api/core/Component\n - api/core/Input\n - api/core/NgModule\n - api/core/OnInit\n - api/forms/FormsModule\n - api/forms/NgModel\n - api/platform-browser/BrowserModule\n - guide/inputs-outputs\n - guide/property-binding\n - tutorial/toh-pt2\n - tutorial/toh-pt3#add-the-input-hero-property\n - tutorial/toh-pt3#create-a-feature-component\n - tutorial/toh-pt3#final-code-review\n - tutorial/toh-pt3#heroes-component-template\n - tutorial/toh-pt3#make-the-herodetailcomponent\n - tutorial/toh-pt3#show-the-herodetailcomponent\n - tutorial/toh-pt3#summary\n - tutorial/toh-pt3#update-the-heroescomponent-template\n - tutorial/toh-pt3#what-changed\n - tutorial/toh-pt3#write-the-template\n - https://github.com/angular/angular/edit/master/aio/content/tutorial/toh-pt3.md?message=docs%3A%20describe%20your%20change...\n-->"
}