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 page. If your structure doesn't match, go back to that page to figure out what you missed.
To display a list of heroes, you'll add heroes to the view's template.
Create a list of ten heroes.
mockHeroes list 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.
Create a public property in
AppComponent that exposes the heroes for binding.
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 list of heroes in the component to the template, iterate over them, and display them individually.
<li> tag by adding the built-in directive
*) prefix to
ngFor is a critical part of this syntax.
It indicates that the
<li> element and its children
constitute a master template.
ngFor directive iterates over the component's
and renders an instance of this template for each hero in that list.
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.
<li> tags, add content
that uses the
hero template variable to display the hero's properties.
Refresh the browser, and 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 argument of the
to the following CSS classes:
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.
Handle click events
Add a click event binding to the
<li> like this:
The parentheses identify the
click event as the target.
onSelect(hero) expression calls the
passing the template input variable
hero, as an argument.
That's the same
hero variable you defined previously in the
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
The hero names should all be unselected before the user picks a hero, so
you won't initialize the
selectedHero as you did with
onSelect() method that sets the
selectedHero property to the
hero that the user clicks.
The template still refers to the old
Bind to the new
selectedHero property instead as follows:
Hide the empty detail with ngIf
When the app loads,
selectedHero is null.
The selected hero is initialized when the user clicks a hero's name.
Angular can't display properties of the null
selectedHero and throws the following error,
visible in the browser's console:
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
selectedHero != null.
Don't forget the asterisk (
*) in front of
Refresh the browser. The app no longer fails and the list of names displays again in the browser.
When there is no selected hero, 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 non-null and
ngIf puts the hero detail content into the DOM and evaluates the nested bindings.
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.
styles metadata that you added above, there is a custom CSS class named
To make the selected hero more visible, you'll apply this
selected class to the
<li> 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
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
Read more about the
[class] binding in the Template Syntax guide.
The final version of the
<li> looks like this:
After clicking "Magneta", the list should look like this:
Here's the complete
app_component.dart 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
ngForin 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. An app shouldn't be one monolithic component. In the next page, you'll split the app into subcomponents and make them work together.