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.
Before adding new features, you’ll benefit from refactoring the app a little.
App template file
You’ll be making several updates to the app component’s template. First, move the template to its own file:
template with a
to the new template file:
open_in_browser Refresh the browser. The app still runs.
Separate concerns and move the
Hero class from
into its own file.
lib/src folder containing the
Back in the app component, add an import using a relative path to the newly created file:
open_in_browser Refresh the browser. The app still runs, and you are now ready to add new features.
To display a list of heroes, you’ll add heroes to the view’s template.
Create a list of ten heroes in the following file under
Eventually this app will fetch the list of heroes from a web service, but for now you can display mock heroes.
App heroes field
hero field with a public
heroes field in
and initialize it with mock heroes (don’t forget the import):
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, replace all of the current template with the following HTML:
Next you’ll add 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 core 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> element, add content
that uses the
hero template variable to display the hero’s properties.
open_in_browser 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, you could set the
styles argument of the
But this makes the Dart file longer and less readable when adding many styles.
Instead, place the styles in a
.css file, and refer to the file using the
styleUrls argument to
@Component. By convention, the names of the
component’s CSS and Dart files have the same base (
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:
EXCEPTION: TypeError: Cannot read property 'name' of undefined in [null]
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 core directive and set it to
selectedHero != null.
Don’t forget the asterisk (
*) in front of
open_in_browser 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 binding to the
When the expression (
hero === selectedHero) is
true, Angular adds the
selected CSS class.
When the expression is
false, Angular removes the
=== operator tests whether the given objects are identical.
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:
Review the app structure
Your project should have the following files:
Here are the files discussed in this page:
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 moved the app template into its own file.
- You moved the
Heroclass into its own file under
- You added the ability to select a hero and show the hero’s details.
- You learned how to use the core directives
ngForin a component’s template.
- You defined styles in a CSS file and made used them to style the app.
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.