{ "id": "tutorial/toh-pt3", "title": "Create a feature component", "contents": "\n\n\n
\n mode_edit\n
\n\n\n
\n

Create a feature componentlink

\n

At the moment, the HeroesComponent displays both the list of heroes and the selected hero's details.

\n

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.

\n

In this page, you'll take the first step in that direction by moving the hero details into a separate, reusable HeroDetailComponent.

\n

The HeroesComponent will only present the list of heroes.\nThe HeroDetailComponent will present details of a selected hero.

\n
\n

For the sample application that this page describes, see the .

\n
\n

Make the HeroDetailComponentlink

\n

Use the Angular CLI to generate a new component named hero-detail.

\n\n ng generate component hero-detail\n\n

The command scaffolds the following:

\n\n

Inside that directory four files are generated:

\n\n

The command also adds the HeroDetailComponent as a declaration in the @NgModule decorator of the src/app/app.module.ts file.

\n

Write the templatelink

\n

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.

\n

The pasted HTML refers to a selectedHero.\nThe new HeroDetailComponent can present any hero, not just a selected hero.\nSo replace \"selectedHero\" with \"hero\" everywhere in the template.

\n

When you're done, the HeroDetailComponent template should look like this:

\n\n<div *ngIf=\"hero\">\n\n <h2>{{hero.name | uppercase}} Details</h2>\n <div><span>id: </span>{{hero.id}}</div>\n <div>\n <label for=\"hero-name\">Hero name: </label>\n <input id=\"hero-name\" [(ngModel)]=\"hero.name\" placeholder=\"name\">\n </div>\n\n</div>\n\n\n\n

Add the @Input() hero propertylink

\n

The HeroDetailComponent template binds to the component's hero property\nwhich is of type Hero.

\n

Open the HeroDetailComponent class file and import the Hero symbol.

\n\nimport { Hero } from '../hero';\n\n\n

The hero property\nmust be an Input property,\nannotated with the @Input() decorator,\nbecause the external HeroesComponent will bind to it like this.

\n\n<app-hero-detail [hero]=\"selectedHero\"></app-hero-detail>\n\n\n

Amend the @angular/core import statement to include the Input symbol.

\n\nimport { Component, OnInit, Input } from '@angular/core';\n\n\n

Add a hero property, preceded by the @Input() decorator.

\n\n@Input() hero?: Hero;\n\n\n

That's the only change you should make to the HeroDetailComponent class.\nThere are no more properties. There's no presentation logic.\nThis component only receives a hero object through its hero property and displays it.

\n

Show the HeroDetailComponentlink

\n

The HeroesComponent 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 HeroDetailComponent.

\n

The two components will have a parent/child relationship.\nThe parent HeroesComponent will control the child HeroDetailComponent\nby sending it a new hero to display whenever\nthe user selects a hero from the list.

\n

You won't change the HeroesComponent class but you will change its template.

\n\n

Update the HeroesComponent templatelink

\n

The HeroDetailComponent selector is 'app-hero-detail'.\nAdd an <app-hero-detail> element near the bottom of the HeroesComponent template, where the hero detail view used to be.

\n

Bind the HeroesComponent.selectedHero to the element's hero property like this.

\n\n<app-hero-detail [hero]=\"selectedHero\"></app-hero-detail>\n\n\n

[hero]=\"selectedHero\" is an Angular property binding.

\n

It's a one way data binding from\nthe selectedHero property of the HeroesComponent to the hero property of the target element, which maps to the hero property of the HeroDetailComponent.

\n

Now when the user clicks a hero in the list, the selectedHero changes.\nWhen the selectedHero changes, the property binding updates hero\nand the HeroDetailComponent displays the new hero.

\n

The revised HeroesComponent template should look like this:

\n\n<h2>My Heroes</h2>\n\n<ul class=\"heroes\">\n <li *ngFor=\"let hero of heroes\"\n [class.selected]=\"hero === selectedHero\"\n (click)=\"onSelect(hero)\">\n <span class=\"badge\">{{hero.id}}</span> {{hero.name}}\n </li>\n</ul>\n\n<app-hero-detail [hero]=\"selectedHero\"></app-hero-detail>\n\n\n\n

The browser refreshes and the application starts working again as it did before.

\n

What changed?link

\n

As before, whenever a user clicks on a hero name,\nthe hero detail appears below the hero list.\nNow the HeroDetailComponent is presenting those details instead of the HeroesComponent.

\n

Refactoring the original HeroesComponent into two components yields benefits, both now and in the future:

\n
    \n
  1. \n

    You reduced the HeroesComponent responsibilities.

    \n
  2. \n
  3. \n

    You can evolve the HeroDetailComponent into a rich hero editor\nwithout touching the parent HeroesComponent.

    \n
  4. \n
  5. \n

    You can evolve the HeroesComponent without touching the hero detail view.

    \n
  6. \n
  7. \n

    You can re-use the HeroDetailComponent in the template of some future component.

    \n
  8. \n
\n

Final code reviewlink

\n

Here are the code files discussed on this page.

\n\n\n \nimport { Component, OnInit, Input } from '@angular/core';\nimport { Hero } from '../hero';\n\n@Component({\n selector: 'app-hero-detail',\n templateUrl: './hero-detail.component.html',\n styleUrls: ['./hero-detail.component.css']\n})\nexport class HeroDetailComponent implements OnInit {\n @Input() hero?: Hero;\n\n constructor() { }\n\n ngOnInit() {\n }\n\n}\n\n\n\n\n \n<div *ngIf=\"hero\">\n\n <h2>{{hero.name | uppercase}} Details</h2>\n <div><span>id: </span>{{hero.id}}</div>\n <div>\n <label for=\"hero-name\">Hero name: </label>\n <input id=\"hero-name\" [(ngModel)]=\"hero.name\" placeholder=\"name\">\n </div>\n\n</div>\n\n\n\n\n \n<h2>My Heroes</h2>\n\n<ul class=\"heroes\">\n <li *ngFor=\"let hero of heroes\"\n [class.selected]=\"hero === selectedHero\"\n (click)=\"onSelect(hero)\">\n <span class=\"badge\">{{hero.id}}</span> {{hero.name}}\n </li>\n</ul>\n\n<app-hero-detail [hero]=\"selectedHero\"></app-hero-detail>\n\n\n\n\n \nimport { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\nimport { FormsModule } 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@NgModule({\n declarations: [\n AppComponent,\n HeroesComponent,\n HeroDetailComponent\n ],\n imports: [\n BrowserModule,\n FormsModule\n ],\n providers: [],\n bootstrap: [AppComponent]\n})\nexport class AppModule { }\n\n\n\n\n\n

Summarylink

\n\n\n\n\n \n
\n\n\n" }