@title
Master/Detail
@intro
Build a master/detail page with a list of heroes.
@description
In this page, you'll expand the Tour of Heroes app to display a list of heroes, and
allow users to select a hero and display the hero's details.
When you're done with this page, the app should look like this .
## Where you left off
Before you continue with this page of the Tour of Heroes,
verify that you have the following structure after [The Hero Editor](tutorial/toh-pt1) page.
If your structure doesn't match, go back to that page to figure out what you missed.
angular-tour-of-heroes
src
app
app.component.ts
app.module.ts
main.ts
index.html
styles.css
systemjs.config.js
tsconfig.json
node_modules ...
package.json
## Keep the app transpiling and running
Enter the following command in the terminal window:
npm start
This command runs the TypeScript compiler in "watch mode", recompiling automatically when the code changes.
The command simultaneously launches the app in a browser and refreshes the browser when the code changes.
You can keep building the Tour of Heroes without pausing to recompile or refresh the browser.
## Displaying heroes
To display a list of heroes, you'll add heroes to the view's template.
### Create heroes
Create an array of ten heroes.
The `HEROES` array is of type `Hero`, the class defined in the previous page.
Eventually this app will fetch the list of heroes from a web service, but for now
you can display mock heroes.
### Expose heroes
Create a public property in `AppComponent` that exposes the heroes for binding.
The `heroes` type isn't defined because TypeScript infers it from the `HEROES` array.
~~~ {.l-sub-section}
The hero data is separated from the class implementation
because ultimately the hero names will come from a data service.
~~~
### Display hero names in a template
To display the hero names in an unordered list,
insert the following chunk of HTML below the title and above the hero details.
Now you can fill the template with hero names.
### List heroes with ngFor
The goal is to bind the array of `heroes` in the component to the template, iterate over them,
and display them individually.
Modify the `
` tag by adding the built-in directive `*ngFor`.
~~~ {.l-sub-section}
The (`*`) prefix to `ngFor` is a critical part of this syntax.
It indicates that the `
` element and its children
constitute a master template.
The `ngFor` directive iterates over the component's `heroes` array
and renders an instance of this template for each hero in that array.
The `let hero` part of the expression identifies `hero` as the template input variable,
which holds the current hero item for each iteration.
You can reference this variable within the template to access the current hero's properties.
Read more about `ngFor` and template input variables in the
[Showing an array property with *ngFor](guide/displaying-data) section of the
[Displaying Data](guide/displaying-data) page and the
[ngFor](guide/template-syntax) section of the
[Template Syntax](guide/template-syntax) page.
~~~
Within the `
` tags, add content
that uses the `hero` template variable to display the hero's properties.
When the browser refreshes, a list of heroes appears.
### Style the heroes
Users should get a visual cue of which hero they are hovering over and which hero is selected.
To add styles to your component, set the `styles` property on the `@Component` decorator
to the following CSS classes:
Remember to use the backtick notation for multi-line strings.
Adding these styles makes the file much longer. In a later page you'll move the styles to a separate file.
When you assign styles to a component, they are scoped to that specific component.
These styles apply only to the `AppComponent` and don't affect the outer HTML.
The template for displaying heroes should look like this:
## Selecting a hero
The app now displays a list of heroes as well as a single hero in the details view. But
the list and the details view are not connected.
When users select a hero from the list, the selected hero should appear in the details view.
This UI pattern is known as "master/detail."
In this case, the _master_ is the heroes list and the _detail_ is the selected hero.
Next you'll connect the master to the detail through a `selectedHero` component property,
which is bound to a click event.
### Add a click event
Add a click event binding to the `
` like this:
The parentheses identify the `
` element's `click` event as the target.
The `onSelect(hero)` expression calls the `AppComponent` method, `onSelect()`,
passing the template input variable `hero`, as an argument.
That's the same `hero` variable you defined previously in the `ngFor` directive.
~~~ {.l-sub-section}
Learn more about event binding at the
[User Input](guide/user-input) page and the
[Event binding](guide/template-syntax) section of the
[Template Syntax](guide/template-syntax) page.
~~~
### Add a click handler to expose the selected hero
You no longer need the `hero` property because you're no longer displaying a single hero; you're displaying a list of heroes.
But the user will be able to select one of the heroes by clicking on it.
So replace the `hero` property with this simple `selectedHero` property:
The hero names should all be unselected before the user picks a hero, so
you won't initialize the `selectedHero` as you did with `hero`.
Add an `onSelect` method that sets the `selectedHero` property to the `hero` that the user clicks.
The template still refers to the old `hero` property.
Bind to the new selectedHero property instead as follows:
### Hide the empty detail with ngIf
When the app loads, the `selectedHero` is undefined and won't be defined until you click a hero's name.
Angular can't display properties of the undefined `selectedHero` and throws the following error,
visible in the browser's console:
EXCEPTION: TypeError: Cannot read property 'name' of undefined in [null]
Although `selectedHero.name` is displayed in the template,
you must keep the hero detail out of the DOM until there is a selected hero.
Wrap the HTML hero detail content of the template with a `
`.
Then add the `ngIf` built-in directive and set it to the `selectedHero` property of the component.
~~~ {.alert.is-critical}
Don't forget the asterisk (`*`) in front of `ngIf`.
~~~
The app no longer fails and the list of names displays again in the browser.
When there is no `selectedHero`, the `ngIf` directive removes the hero detail HTML from the DOM.
There are no hero detail elements or bindings to worry about.
When the user picks a hero, `selectedHero` becomes defined and
`ngIf` puts the hero detail content into the DOM and evaluates the nested bindings.
~~~ {.l-sub-section}
Read more about `ngIf` and `ngFor` in the
[Structural Directives](guide/structural-directives) page and the
[Built-in directives](guide/template-syntax) section of the
[Template Syntax](guide/template-syntax) page.
~~~
### Style the selected hero
While the selected hero details appear below the list, it's difficult to identify the selected hero within the list itself.
In the `styles` metadata that you added above, there is a custom CSS class named `selected`.
To make the selected hero more visible, you'll apply this `selected` class to the `
` when the user clicks on a hero name.
For example, when the user clicks "Magneta", it should render with a distinctive but subtle background color
like this:
In the template, add the following `[class.selected]` binding to the `
`:
When the expression (`hero === selectedHero`) is `true`, Angular adds the `selected` CSS class.
When the expression is `false`, Angular removes the `selected` class.
~~~ {.l-sub-section}
Read more about the `[class]` binding in the [Template Syntax](guide/template-syntax) guide.
~~~
The final version of the `
` looks like this:
After clicking "Magneta", the list should look like this:
Here's the complete `app.component.ts` as of now:
## The road you've travelled
Here's what you achieved in this page:
* The Tour of Heroes app displays a list of selectable heroes.
* You added the ability to select a hero and show the hero's details.
* You learned how to use the built-in directives `ngIf` and `ngFor` in a component's template.
Your app should look like this .
## The road ahead
You've expanded the Tour of Heroes app, but it's far from complete.
You can't put the entire app into a single component.
In the [next page](tutorial/toh-pt3), you'll split the app into sub-components and make them work together.