docs(aio): update migrated content from anguar.io

This commit is contained in:
Peter Bacon Darwin
2017-03-27 16:08:53 +01:00
committed by Pete Bacon Darwin
parent ff82756415
commit fd72fad8fd
1901 changed files with 20145 additions and 45127 deletions

View File

@ -5,78 +5,80 @@ Tutorial: Tour of Heroes
The Tour of Heroes tutorial takes you through the steps of creating an Angular application in TypeScript.
@description
Our grand plan for this tutorial is to build an app to help a staffing agency manage its stable of heroes.
Even heroes need to find work.
Of course we'll only make a little progress in this tutorial. What we do build will
have many of the features we expect to find in a full-blown, data-driven application: acquiring and displaying
The grand plan for this tutorial is to build an app that helps a staffing agency manage its stable of heroes.
The Tour of Heroes app covers the core fundamentals of Angular. You'll build a basic app that
has many of the features you'd expect to find in a full-blown, data-driven app: acquiring and displaying
a list of heroes, editing a selected hero's detail, and navigating among different
views of heroic data.
The Tour of Heroes covers the core fundamentals of Angular.
Well use built-in directives to show/hide elements and display lists of hero data.
Well create a component to display hero details and another to show an array of heroes.
We'll use one-way data binding for read-only data. We'll add editable fields to update a model
with two-way data binding. We'll bind component methods to user events like key strokes and clicks.
Well learn to select a hero from a master list and edit that hero in the details view. We'll
format data with pipes. We'll create a shared service to assemble our heroes. And we'll use routing to navigate among different views and their components.
You'll use built-in directives to show and hide elements and display lists of hero data.
You'll create components to display hero details and show an array of heroes.
You'll use one-way data binding for read-only data. You'll add editable fields to update a model
with two-way data binding. You'll bind component methods to user events, like keystrokes and clicks.
You'll enable users to select a hero from a master list and edit that hero in the details view. You'll
format data with pipes. You'll create a shared service to assemble the heroes.
And you'll use routing to navigate among different views and their components.
<!-- CF: Should this be a bullet list? -->
Well learn enough core Angular to get started and gain confidence that
Angular can do whatever we need it to do.
We'll be covering a lot of ground at an introductory level but well find plenty of links
to chapters with greater depth.
You'll learn enough core Angular to get started and gain confidence that
Angular can do whatever you need it to do.
You'll cover a lot of ground at an introductory level, and you'll find many links
to pages with greater depth.
Run the <live-example name="toh-6"></live-example>.
When you're done with this tutorial, the app will look like this <live-example name="toh-6"></live-example>.
## The End Game
Here's a visual idea of where we're going in this tour, beginning with the "Dashboard"
view and our most heroic heroes:
## The end game
Here's a visual idea of where this tutorial leads, beginning with the "Dashboard"
view and the most heroic heroes:
<figure class='image-display'>
<img src='assets/images/devguide/toh/heroes-dashboard-1.png' alt="Output of heroes dashboard"> </img>
</figure>
Above the dashboard are two links ("Dashboard" and "Heroes").
We could click them to navigate between this Dashboard and a Heroes view.
You can click the two links above the dashboard ("Dashboard" and "Heroes")
to navigate between this Dashboard view and a Heroes view.
Instead we click the dashboard hero named "Magneta" and the router takes us to a "Hero Details" view
of that hero where we can change the hero's name.
If you click the dashboard hero "Magneta," the router opens a "Hero Details" view
where you can change the hero's name.
<figure class='image-display'>
<img src='assets/images/devguide/toh/hero-details-1.png' alt="Details of hero in app"> </img>
</figure>
Clicking the "Back" button would return us to the "Dashboard".
Links at the top can take us to either of the main views.
We'll click "Heroes". The app takes to the "Heroes" master list view.
Clicking the "Back" button returns you to the Dashboard.
Links at the top take you to either of the main views.
If you click "Heroes," the app displays the "Heroes" master list view.
<figure class='image-display'>
<img src='assets/images/devguide/toh/heroes-list-2.png' alt="Output of heroes list app"> </img>
</figure>
We click a different hero and the readonly mini-detail beneath the list reflects our new choice.
When you click a different hero name, the read-only mini detail beneath the list reflects the new choice.
We click the "View Details" button to drill into the
editable details of our selected hero.
You can click the "View Details" button to drill into the
editable details of the selected hero.
The following diagram captures all of our navigation options.
The following diagram captures all of the navigation options.
<figure class='image-display'>
<img src='assets/images/devguide/toh/nav-diagram.png' alt="View navigations"> </img>
</figure>
Here's our app in action
Here's the app in action:
<figure class='image-display'>
<img src='assets/images/devguide/toh/toh-anim.gif' alt="Tour of Heroes in Action"> </img>
</figure>
## Up Next
## Up next
Well build this Tour of Heroes together, step by step.
We'll motivate each step with a requirement that we've
met in countless applications. Everything has a reason.
You'll build the Tour of Heroes app, step by step.
Each step is motivated with a requirement that you've likely
met in many applications. Everything has a reason.
And well meet many of the core fundamentals of Angular along the way.
Along the way, you'll become familiar with many of the core fundamentals of Angular.

View File

@ -2,15 +2,14 @@
The Hero Editor
@intro
We build a simple hero editor.
Build a simple hero editor.
@description
## Setup to develop locally
Real application development takes place in a local development environment like your machine.
Follow the [setup](guide/setup) instructions for creating a new project
named <ngio-ex path="angular-tour-of-heroes"></ngio-ex>
after which the file structure should look like this:
named <ngio-ex path="angular-tour-of-heroes"></ngio-ex>.
The file structure should look like this:
<aio-filetree>
@ -62,7 +61,7 @@ after which the file structure should look like this:
<aio-file>
node_modules ...
node_modules ...
</aio-file>
@ -76,154 +75,179 @@ after which the file structure should look like this:
</aio-filetree>
When we're done with this first episode, the app runs like this <live-example></live-example>.
When you're done with this page, the app should look like this <live-example></live-example>.
{@a keep-transpiling}
## Keep the app transpiling and running
We want to start the TypeScript compiler, have it watch for changes, and start our server.
Do this by entering the following command in the terminal window.
Enter the following command in the terminal window:
<code-example language="sh" class="code-shell">
npm start
</code-example>
This command runs the compiler in watch mode, starts the server, launches the app in a browser,
and keeps the app running while we continue to build the Tour of Heroes.
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.
## Show our Hero
We want to display Hero data in our app
You can keep building the Tour of Heroes without pausing to recompile or refresh the browser.
Update the `AppComponent` so it has two properties: &nbsp; a `title` property for the application name and a `hero` property
for a hero named "Windstorm".
## Show the hero
Add two properties to the `AppComponent`: a `title` property for the app name and a `hero` property
for a hero named "Windstorm."
{@example 'toh-1/ts-snippets/app.component.snippets.pt1.ts' region='app-component-1'}
<code-example path="toh-1/app/app.component.1.ts" region="app-component-1" linenums="false">
Now update the template in the `@Component` decoration with data bindings to these new properties.
{@example 'toh-1/ts-snippets/app.component.snippets.pt1.ts' region='show-hero'}
The browser should refresh and display our title and hero.
The double curly braces tell our app to read the `title` and `hero` properties from the component and render them.
This is the "interpolation" form of one-way data binding.
Learn more about interpolation in the [Displaying Data chapter](guide/displaying-data).### Hero object
At the moment, our hero is just a name. Our hero needs more properties.
Let's convert the `hero` from a literal string to a class.
Create a `Hero` class with `id` and `name` properties.
For now put this near the top of the `app.component.ts` file, just below the import statement.
{@example 'toh-1/ts/src/app/app.component.ts' region='hero-class-1'}
Now that we have a `Hero` class, lets refactor our components `hero` property to be of type `Hero`.
Then initialize it with an id of `1` and the name, "Windstorm".
{@example 'toh-1/ts/src/app/app.component.ts' region='hero-property-1'}
Because we changed the hero from a string to an object,
we update the binding in the template to refer to the heros `name` property.
{@example 'toh-1/ts-snippets/app.component.snippets.pt1.ts' region='show-hero-2'}
The browser refreshes and continues to display our heros name.
### Adding more HTML
Displaying a name is good, but we want to see all of our heros properties.
Well add a `<div>` for our heros `id` property and another `<div>` for our heros `name`.
{@example 'toh-1/ts-snippets/app.component.snippets.pt1.ts' region='show-hero-properties'}
Uh oh, our template string is getting long. We better take care of that to avoid the risk of making a typo in the template.
### Multi-line template strings
We could make a more readable template with string concatenation
but that gets ugly fast, it is harder to read, and
it is easy to make a spelling error. Instead,
lets take advantage of the template strings feature
in ES2015 and TypeScript to maintain our sanity.
Change the quotes around the template to back-ticks and
put the `<h1>`, `<h2>` and `<div>` elements on their own lines.
{@example 'toh-1/ts-snippets/app.component.snippets.pt1.ts' region='multi-line-strings'}
## Editing Our Hero
We want to be able to edit the hero name in a textbox.
Refactor the hero name `<label>` with `<label>` and `<input>` elements as shown below:
{@example 'toh-1/ts-snippets/app.component.snippets.pt1.ts' region='editing-Hero'}
We see in the browser that the heros name does appear in the `<input>` textbox.
But something doesnt feel right.
When we change the name, we notice that our change
is not reflected in the `<h2>`. We won't get the desired behavior
with a one-way binding to `<input>`.
### Two-Way Binding
We intend to display the name of the hero in the `<input>`, change it,
and see those changes wherever we bind to the heros name.
In short, we want two-way data binding.
Before we can use two-way data binding for **form inputs**, we need to import the `FormsModule`
package in our Angular module. We add it to the `NgModule` decorator's `imports` array. This array contains the list
of external modules used by our application.
Now we have included the forms package which includes `ngModel`.
{@example 'toh-1/ts/src/app/app.module.ts'}
Learn more about the `FormsModule` and `ngModel` in the
[Forms](guide/forms) and
[Template Syntax](guide/template-syntax) chapters.
Lets update the template to use the **`ngModel`** built-in directive for two-way binding.
Replace the `<input>` with the following HTML
<code-example language="html">
&lt;input [(ngModel)]="hero.name" placeholder="name">
</code-example>
The browser refreshes. We see our hero again. We can edit the heros name and
see the changes reflected immediately in the `<h2>`.
Now update the template in the `@Component` decorator with data bindings to these new properties.
## The Road Weve Travelled
Lets take stock of what weve built.
* Our Tour of Heroes uses the double curly braces of interpolation (a kind of one-way data binding)
to display the application title and properties of a `Hero` object.
* We wrote a multi-line template using ES2015s template strings to make our template readable.
* We can both display and change the heros name after adding a two-way data binding to the `<input>` element
using the built-in `ngModel` directive.
* The `ngModel` directive also propagates changes to every other binding of the `hero.name`.
<code-example path="toh-1/app/app.component.1.ts" region="show-hero" linenums="false">
Run the <live-example></live-example> for this part.
</code-example>
The browser refreshes and displays the title and hero name.
The double curly braces are Angular's *interpolation binding* syntax.
These interpolation bindings present the component's `title` and `hero` property values,
as strings, inside the HTML header tags.
~~~ {.l-sub-section}
Read more about interpolation in the [Displaying Data](guide/displaying-data) page.
~~~
### Hero object
The hero needs more properties.
Convert the `hero` from a literal string to a class.
Create a `Hero` class with `id` and `name` properties.
Add these properties near the top of the `app.component.ts` file, just below the import statement.
<code-example path="toh-1/src/app/app.component.ts" region="hero-class-1" linenums="false">
</code-example>
In the `Hero` class, refactor the component's `hero` property to be of type `Hero`,
then initialize it with an `id` of `1` and the name `Windstorm`.
<code-example path="toh-1/src/app/app.component.ts" region="hero-property-1" linenums="false">
</code-example>
Because you changed the hero from a string to an object,
update the binding in the template to refer to the hero's `name` property.
<code-example path="toh-1/app/app.component.1.ts" region="show-hero-2">
</code-example>
The browser refreshes and continues to display the hero's name.
### Adding HTML with multi-line template strings
To show all of the hero's properties,
add a `<div>` for the hero's `id` property and another `<div>` for the hero's `name`.
To keep the template readable, place each `<div>` on its own line.
The backticks around the component template let you put the `<h1>`, `<h2>`, and `<div>` elements on their own lines,
thanks to the <i>template literals</i> feature in ES2015 and TypeScript. For more information, see
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals" target="_blank" title="template literal">Template literals</a>.
<code-example path="toh-1/app/app.component.1.ts" region="multi-line-strings" linenums="false">
</code-example>
## Edit the hero name
Users should be able to edit the hero name in an `<input>` textbox.
The textbox should both _display_ the hero's `name` property
and _update_ that property as the user types.
You need a two-way binding between the `<input>` form element and the `hero.name` property.
### Two-way binding
Refactor the hero name in the template so it looks like this:
<code-example path="toh-1/app/app.component.1.ts" region="name-input" linenums="false">
</code-example>
`[(ngModel)]` is the Angular syntax to bind the `hero.name` property
to the textbox.
Data flow _in both directions_: from the property to the textbox;
and from the textbox back to the property.
Unfortunately, immediately after this change, the application breaks.
If you looked in the browser console, you'd see Angular complaining that
"`ngModel` ... isn't a known property of `input`."
Although `NgModel` is a valid Angular directive, it isn't available by default.
It belongs to the optional `FormsModule`.
You must opt-in to using that module.
### Import the _FormsModule_
Open the `app.module.ts` file and import the `FormsModule` symbol from the `@angular/forms` library.
Then add the `FormsModule` to the `@NgModule` metadata's `imports` array, which contains the list
of external modules that the app uses.
The updated `AppModule` looks like this:
<code-example path="toh-1/src/app/app.module.ts">
</code-example>
~~~ {.l-sub-section}
Read more about `FormsModule` and `ngModel` in the
[Two-way data binding with ngModel](guide/forms) section of the
[Forms](guide/forms) guide and the
[Two-way binding with NgModel](guide/template-syntax) section of the
[Template Syntax](guide/template-syntax) guide.
~~~
When the browser refreshes, the app should work again.
You can edit the hero's name and see the changes reflected immediately in the `<h2>` above the textbox.
## The road you've travelled
Take stock of what you've built.
* The Tour of Heroes app uses the double curly braces of interpolation (a type of one-way data binding)
to display the app title and properties of a `Hero` object.
* You wrote a multi-line template using ES2015's template literals to make the template readable.
* You added a two-way data binding to the `<input>` element
using the built-in `ngModel` directive. This binding both displays the hero's name and allows users to change it.
* The `ngModel` directive propagates changes to every other binding of the `hero.name`.
Your app should look like this <live-example></live-example>.
Here's the complete `app.component.ts` as it stands now:
{@example 'toh-1/ts/src/app/app.component.ts' region='pt1'}
<code-example path="toh-1/src/app/app.component.ts">
</code-example>
## The Road Ahead
Our Tour of Heroes only displays one hero and we really want to display a list of heroes.
We also want to allow the user to select a hero and display their details.
Well learn more about how to retrieve lists, bind them to the
template, and allow a user to select a hero in the
[next tutorial chapter](tutorial/toh-pt2).
## The road ahead
In the [next tutorial page](tutorial/toh-pt2), you'll build on the Tour of Heroes app to display a list of heroes.
You'll also allow the user to select heroes and display their details.
You'll learn more about how to retrieve lists and bind them to the template.

View File

@ -2,23 +2,18 @@
Master/Detail
@intro
We build a master/detail page with a list of heroes.
Build a master/detail page with a list of heroes.
@description
Our story needs more heroes.
Well expand our Tour of Heroes app to display a list of heroes,
allow the user to select a hero, and display the heros details.
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.
Run the <live-example></live-example> for this part.
When you're done with this page, the app should look like this <live-example></live-example>.
Lets take stock of what well need to display a list of heroes.
First, we need a list of heroes. We want to display those heroes in the views template,
so well need a way to do that.
## Where We Left Off
Before we continue with Part 2 of the Tour of Heroes,
lets verify we have the following structure after [Part 1](tutorial/toh-pt1).
If not, well need to go back to Part 1 and figure out what we missed.
## 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.
<aio-filetree>
@ -70,7 +65,7 @@ If not, well need to go back to Part 1 and figure out what we missed.
<aio-file>
node_modules ...
node_modules ...
</aio-file>
@ -84,273 +79,313 @@ If not, well need to go back to Part 1 and figure out what we missed.
</aio-filetree>
### Keep the app transpiling and running
We want to start the TypeScript compiler, have it watch for changes, and start our server. We'll do this by typing
## Keep the app transpiling and running
Enter the following command in the terminal window:
<code-example language="sh" class="code-shell">
npm start
</code-example>
This will keep the application running while we continue to build the Tour of Heroes.
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.
## Displaying Our Heroes
### Creating heroes
Lets create an array of ten heroes.
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.
{@example 'toh-2/ts/src/app/app.component.ts' region='hero-array'}
<code-example path="toh-2/src/app/app.component.ts" region="hero-array">
The `HEROES` array is of type `Hero`, the class defined in part one,
to create an array of heroes.
We aspire to fetch this list of heroes from a web service, but lets take small steps
first and display mock heroes.
</code-example>
### Exposing heroes
Lets create a public property in `AppComponent` that exposes the heroes for binding.
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.
{@example 'toh-2/ts-snippets/app.component.snippets.pt2.ts' region='hero-array-1'}
We did not have to define the `heroes` type. TypeScript can infer it from the `HEROES` array.
We could have defined the heroes list here in this component class.
But we know that ultimately well get the heroes from a data service.
Because we know where we are heading, it makes sense to separate the hero data
from the class implementation from the start.### Displaying heroes in a template
Our component has `heroes`. Lets create an unordered list in our template to display them.
Well insert the following chunk of HTML below the title and above the hero details.
{@example 'toh-2/ts-snippets/app.component.snippets.pt2.ts' region='heroes-template-1'}
Now we have a template that we can fill with our heroes.
### Listing heroes with ngFor
We want to bind the array of `heroes` in our component to our template, iterate over them,
and display them individually.
Well need some help from Angular to do this. Lets do this step by step.
First modify the `<li>` tag by adding the built-in directive `*ngFor`.
{@example 'toh-2/ts-snippets/app.component.snippets.pt2.ts' region='heroes-ngfor-1'}
### Expose heroes
Create a public property in `AppComponent` that exposes the heroes for binding.
~~~ {.alert.is-critical}
<code-example path="toh-2/app/app.component.1.html" region="hero-array-1">
The leading asterisk (`*`) in front of `ngFor` is a critical part of this syntax.
</code-example>
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.
The (`*`) prefix to `ngFor` indicates that the `<li>` element and its children
<code-example path="toh-2/app/app.component.1.html" region="heroes-template-1" linenums="false">
</code-example>
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 `<li>` tag by adding the built-in directive `*ngFor`.
<code-example path="toh-2/app/app.component.1.html" region="heroes-ngfor-1">
</code-example>
~~~ {.l-sub-section}
The (`*`) prefix to `ngFor` is a critical part of this syntax.
It indicates that the `<li>` element and its children
constitute a master template.
The `ngFor` directive iterates over the `heroes` array returned by the `AppComponent.heroes` property
and stamps out instances of this 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 quoted text assigned to `ngFor` means
“*take each hero in the `heroes` array, store it in the local `hero` variable,
and make it available to the corresponding template instance*”.
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.
The `let` keyword before "hero" identifies `hero` as a template input variable.
We can reference this variable within the template to access a heros properties.
Learn more about `ngFor` and template input variables in the
[Displaying Data](guide/displaying-data) and
[Template Syntax](guide/template-syntax) chapters.
Now we insert some content between the `<li>` tags
that uses the `hero` template variable to display the heros 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.
{@example 'toh-2/ts-snippets/app.component.snippets.pt2.ts' region='ng-for'}
~~~
When the browser refreshes, we see a list of heroes!
Within the `<li>` tags, add content
that uses the `hero` template variable to display the hero's properties.
### Styling our heroes
Our list of heroes looks pretty bland.
We want to make it visually obvious to a user which hero we are hovering over and which hero is selected.
Lets add some styles to our component by setting the `styles` property on the `@Component` decorator
<code-example path="toh-2/app/app.component.1.html" region="ng-for" linenums="false">
</code-example>
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:
{@example 'toh-2/ts/src/app/app.component.ts' region='styles'}
<code-example path="toh-2/src/app/app.component.ts" region="styles" linenums="false">
Notice that we again use the back-tick notation for multi-line strings.
That's a lot of styles! We can put them inline as shown here, or we can move them out to their own file which will make it easier to code our component.
We'll do this in a later chapter. For now let's keep rolling.
When we assign styles to a component they are scoped to that specific component.
Our styles will only apply to our `AppComponent` and won't "leak" to the outer HTML.
Our template for displaying the heroes should now look like this:
{@example 'toh-2/ts-snippets/app.component.snippets.pt2.ts' region='heroes-styled'}
## Selecting a Hero
We have a list of heroes and we have a single hero displayed in our app.
The list and the single hero are not connected in any way.
We want the user to select a hero from our list, and have the selected hero appear in the details view.
This UI pattern is widely known as "master-detail".
In our case, the master is the heroes list and the detail is the selected hero.
Lets connect the master to the detail through a `selectedHero` component property bound to a click event.
### Click event
We modify the `<li>` by inserting an Angular event binding to its click event.
{@example 'toh-2/ts-snippets/app.component.snippets.pt2.ts' region='selectedHero-click'}
Focus on the event binding
<code-example>
(click)="onSelect(hero)"
</code-example>
The parenthesis identify the `<li>` elements `click` event as the target.
The expression to the right of the equal sign calls the `AppComponent` method, `onSelect()`,
passing the template input variable `hero` as an argument.
Thats the same `hero` variable we defined previously in the `ngFor`.
Learn more about Event Binding in the
[User Input](guide/user-input) and
[Templating Syntax](guide/template-syntax) chapters.### Add the click handler
Our event binding refers to an `onSelect` method that doesnt exist yet.
Well add that method to our component now.
Remember to use the backtick notation for multi-line strings.
What should that method do? It should set the components selected hero to the hero that the user clicked.
Adding these styles makes the file much longer. In a later page you'll move the styles to a separate file.
Our component doesnt have a “selected hero” yet either. Well start there.
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.
### Expose the selected hero
We no longer need the static `hero` property of the `AppComponent`.
**Replace** it with this simple `selectedHero` property:
The template for displaying heroes should look like this:
{@example 'toh-2/ts/src/app/app.component.ts' region='selected-hero'}
Weve decided that none of the heroes should be selected before the user picks a hero so
we wont initialize the `selectedHero` as we were doing with `hero`.
<code-example path="toh-2/app/app.component.1.html" region="heroes-styled" linenums="false">
Now **add an `onSelect` method** that sets the `selectedHero` property to the `hero` the user clicked.
{@example 'toh-2/ts/src/app/app.component.ts' region='on-select'}
We will be showing the selected hero's details in our template.
At the moment, it is still referring to the old `hero` property.
Lets fix the template to bind to the new `selectedHero` property.
</code-example>
{@example 'toh-2/ts-snippets/app.component.snippets.pt2.ts' region='selectedHero-details'}
## 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 `<li>` like this:
<code-example path="toh-2/app/app.component.1.html" region="selectedHero-click" linenums="false">
</code-example>
The parentheses identify the `<li>` 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:
<code-example path="toh-2/src/app/app.component.ts" region="selected-hero">
</code-example>
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.
<code-example path="toh-2/src/app/app.component.ts" region="on-select" linenums="false">
</code-example>
The template still refers to the old `hero` property.
Bind to the new selectedHero property instead as follows:
<code-example path="toh-2/app/app.component.1.html" region="selectedHero-details" linenums="false">
</code-example>
### Hide the empty detail with ngIf
When our app loads we see a list of heroes, but a hero is not selected.
The `selectedHero` is `undefined`.
Thats why we'll see the following error in the browsers console:
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:
<code-example format="nocode">
EXCEPTION: TypeError: Cannot read property 'name' of undefined in [null]
</code-example>
Remember that we are displaying `selectedHero.name` in the template.
This name property does not exist because `selectedHero` itself is undefined.
Although `selectedHero.name` is displayed in the template,
you must keep the hero detail out of the DOM until there is a selected hero.
We'll address this problem by keeping the hero detail out of the DOM until there is a selected hero.
We wrap the HTML hero detail content of our template with a `<div>`.
Then we add the `ngIf` built-in directive and set it to the `selectedHero` property of our component.
Wrap the HTML hero detail content of the template with a `<div>`.
Then add the `ngIf` built-in directive and set it to the `selectedHero` property of the component.
{@example 'toh-2/ts-snippets/app.component.snippets.pt2.ts' region='ng-if'}
<code-example path="toh-2/app/app.component.1.html" region="ng-if" linenums="false">
</code-example>
~~~ {.alert.is-critical}
Remember that the leading asterisk (`*`) in front of `ngIf` is
a critical part of this syntax.
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 will be no hero detail elements and no bindings to worry about.
There are no hero detail elements or bindings to worry about.
When the user picks a hero, `selectedHero` becomes "truthy" and
When the user picks a hero, `selectedHero` becomes defined and
`ngIf` puts the hero detail content into the DOM and evaluates the nested bindings.
`ngIf` and `ngFor` are called “structural directives” because they can change the
structure of portions of the DOM.
In other words, they give structure to the way Angular displays content in the DOM.
Learn more about `ngIf`, `ngFor` and other structural directives in the
[Structural Directives](guide/structural-directives) and
[Template Syntax](guide/template-syntax) chapters.
The browser refreshes and we see the list of heroes but not the selected hero detail.
The `ngIf` keeps it out of the DOM as long as the `selectedHero` is undefined.
When we click on a hero in the list, the selected hero displays in the hero details.
Everything is working as we expect.
### Styling the selection
~~~ {.l-sub-section}
We see the selected hero in the details area below but we cant quickly locate that hero in the list above.
We can fix that by applying the `selected` CSS class to the appropriate `<li>` in the master list.
For example, when we select Magneta from the heroes list,
we can make it pop out visually by giving it a subtle background color as shown here.
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 `<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
like this:
<figure class='image-display'>
<img src='assets/images/devguide/toh/heroes-list-selected.png' alt="Selected hero"> </img>
</figure>
Well add a property binding on `class` for the `selected` class to the template. We'll set this to an expression that compares the current `selectedHero` to the `hero`.
In the template, add the following `[class.selected]` binding to the `<li>`:
The key is the name of the CSS class (`selected`). The value is `true` if the two heroes match and `false` otherwise.
Were saying “*apply the `selected` class if the heroes match, remove it if they dont*”.
<code-example path="toh-2/app/app.component.1.html" region="class-selected-1" linenums="false">
{@example 'toh-2/ts-snippets/app.component.snippets.pt2.ts' region='class-selected-1'}
</code-example>
Notice in the template that the `class.selected` is surrounded in square brackets (`[]`).
This is the syntax for a **property binding**, a binding in which data flows one way
from the data source (the expression `hero === selectedHero`) to a property of `class`.
{@example 'toh-2/ts-snippets/app.component.snippets.pt2.ts' region='class-selected-2'}
When the expression (`hero === selectedHero`) is `true`, Angular adds the `selected` CSS class.
When the expression is `false`, Angular removes the `selected` class.
Learn more about [property bindings](guide/template-syntax)
in the Template Syntax chapter.
The browser reloads our app.
We select the hero Magneta and the selection is clearly identified by the background color.
~~~ {.l-sub-section}
Read more about the `[class]` binding in the [Template Syntax](guide/template-syntax) guide.
~~~
The final version of the `<li>` looks like this:
<code-example path="toh-2/app/app.component.1.html" region="class-selected-2" linenums="false">
</code-example>
After clicking "Magneta", the list should look like this:
<figure class='image-display'>
<img src='assets/images/devguide/toh/heroes-list-1.png' alt="Output of heroes list app"> </img>
</figure>
We select a different hero and the tell-tale color switches to that hero.
Here's the complete `app.component.ts` as it stands now:
Here's the complete `app.component.ts` as of now:
{@example 'toh-2/ts/src/app/app.component.ts'}
<code-example path="toh-2/src/app/app.component.ts">
</code-example>
## The Road Weve Travelled
Heres what we achieved in this chapter:
## The road you've travelled
Here's what you achieved in this page:
* Our Tour of Heroes now displays a list of selectable heroes
* We added the ability to select a hero and show the heros details
* We learned how to use the built-in directives `ngIf` and `ngFor` in a components template
* 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.
Run the <live-example></live-example> for this part.
Your app should look like this <live-example></live-example>.
### The Road Ahead
Our Tour of Heroes has grown, but its far from complete.
We can't put the entire app into a single component.
We need to break it up into sub-components and teach them to work together
as we learn in the [next chapter](tutorial/toh-pt3).
## 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.

View File

@ -2,16 +2,24 @@
Multiple Components
@intro
We refactor the master/detail view into separate components.
Refactor the master/detail view into separate components.
@description
Our app is growing.
Use cases are flowing in for reusing components, passing data to components, and creating more reusable assets. Let's separate the heroes list from the hero details and make the details component reusable.
The `AppComponent` is doing _everything_ at the moment.
In the beginning, it showed details of a single hero.
Then it became a master/detail form with both a list of heroes and the hero detail.
Soon there will be new requirements and capabilities.
You can't keep piling features on top of features in one component; that's not maintainable.
Run the <live-example></live-example> for this part.
You'll ned to break it up into sub-components, each focused on a specific task or workflow.
Eventually, the `AppComponent` could become a simple shell that hosts those sub-components.
## Where We Left Off
Before we continue with our Tour of Heroes, lets verify we have the following structure. If not, well need to go back and follow the previous chapters.
In this page, you'll take the first step in that direction by carving out the hero details into a separate, reusable component.
When you're done, the app should look like this <live-example></live-example>.
## Where you left off
Before getting started on this page, verify that you have the following structure from earlier in the Tour of Heroes.
If not, go back to the previous pages.
<aio-filetree>
@ -63,7 +71,7 @@ Before we continue with our Tour of Heroes, lets verify we have the following
<aio-file>
node_modules ...
node_modules ...
</aio-file>
@ -77,170 +85,217 @@ Before we continue with our Tour of Heroes, lets verify we have the following
</aio-filetree>
### Keep the app transpiling and running
We want to start the TypeScript compiler, have it watch for changes, and start our server. We'll do this by typing
Keep the app transpiling and running while you build the Tour of Heroes
by entering the `npm start` command in a terminal window
[as you did before](tutorial/toh-pt1).
<code-example language="sh" class="code-shell">
npm start
</code-example>
## Make a hero detail component
Add a file named `hero-detail.component.ts` to the `app/` folder.
This file will hold the new `HeroDetailComponent`.
This will keep the application running while we continue to build the Tour of Heroes.
The file and component names follow the standard described in the Angular
[style guide](guide/style-guide).
## Making a Hero Detail Component
Our heroes list and our hero details are in the same component in the same file.
They're small now but each could grow.
We are sure to receive new requirements for one and not the other.
Yet every change puts both components at risk and doubles the testing burden without benefit.
If we had to reuse the hero details elsewhere in our app,
the heroes list would tag along for the ride.
* The component _class_ name should be written in _upper camel case_ and end in the word "Component".
The hero detail component class is `HeroDetailComponent`.
Our current component violates the
[Single Responsibility Principle](https://blog.8thlight.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html).
It's only a tutorial but we can still do things right &mdash;
especially if doing them right is easy and we learn how to build Angular apps in the process.
* The component _file_ name should be spelled in [_lower dash case_](guide/glossary),
each word separated by dashes, and end in `.component.ts`.
The `HeroDetailComponent` class goes in the `hero-detail.component.ts` file.
Lets break the hero details out into its own component.
### Separating the Hero Detail Component
Add a new file named `hero-detail.component.ts` to the `app` folder and create `HeroDetailComponent` as follows.
Start writing the `HeroDetailComponent` as follows:
{@example 'toh-3/ts/src/app/hero-detail.component.ts' region='v1'}
<code-example path="toh-3/app/hero-detail.component.1.ts" region="v1" linenums="false">
### Naming conventions
We like to identify at a glance which classes are components and which files contain components.
Notice that we have an `AppComponent` in a file named `app.component.ts` and our new
`HeroDetailComponent` is in a file named `hero-detail.component.ts`.
All of our component names end in "Component". All of our component file names end in ".component".
We spell our file names in lower **[dash case](guide/glossary)**
(AKA **[kebab-case](guide/glossary)**) so we don't worry about
case sensitivity on the server or in source control.
<!-- TODO
.l-sub-section
:marked
Learn more about naming conventions in the chapter [Naming Conventions]
:marked
-->We begin by importing the `Component` and `Input` decorators from Angular because we're going to need them soon.
We create metadata with the `@Component` decorator where we
specify the selector name that identifies this component's element.
Then we export the class to make it available to other components.
When we finish here, we'll import it into `AppComponent` and create a corresponding `<my-hero-detail>` element.#### Hero Detail Template
At the moment, the *Heroes* and *Hero Detail* views are combined in one template in `AppComponent`.
Lets **cut** the *Hero Detail* content from `AppComponent` and **paste** it into the new template property of `HeroDetailComponent`.
We previously bound to the `selectedHero.name` property of the `AppComponent`.
Our `HeroDetailComponent` will have a `hero` property, not a `selectedHero` property.
So we replace `selectedHero` with `hero` everywhere in our new template. That's our only change.
The result looks like this:
{@example 'toh-3/ts/src/app/hero-detail.component.ts' region='template'}
Now our hero detail layout exists only in the `HeroDetailComponent`.
#### Add the *hero* property
Lets add that `hero` property we were talking about to the component class.
{@example 'toh-3/ts/src/app/hero-detail.component.ts' region='hero'}
Uh oh. We declared the `hero` property as type `Hero` but our `Hero` class is over in the `app.component.ts` file.
We have two components, each in their own file, that need to reference the `Hero` class.
We solve the problem by relocating the `Hero` class from `app.component.ts` to its own `hero.ts` file.
{@example 'toh-3/ts/src/app/hero.ts'}
We export the `Hero` class from `hero.ts` because we'll need to reference it in both component files.
Add the following import statement near the top of **both `app.component.ts` and `hero-detail.component.ts`**.
{@example 'toh-3/ts/src/app/hero-detail.component.ts' region='hero-import'}
#### The *hero* property is an ***input***
The `HeroDetailComponent` must be told what hero to display. Who will tell it? The parent `AppComponent`!
The `AppComponent` knows which hero to show: the hero that the user selected from the list.
The user's selection is in its `selectedHero` property.
We will soon update the `AppComponent` template so that it binds its `selectedHero` property
to the `hero` property of our `HeroDetailComponent`. The binding *might* look like this:
<code-example language="html">
&lt;my-hero-detail [hero]="selectedHero">&lt;/my-hero-detail>
</code-example>
Notice that the `hero` property is the ***target*** of a property binding &mdash; it's in square brackets to the left of the (=).
Angular insists that we declare a ***target*** property to be an ***input*** property.
If we don't, Angular rejects the binding and throws an error.
We explain input properties in more detail [here](guide/attribute-directives)
where we also explain why *target* properties require this special treatment and
*source* properties do not.There are a couple of ways we can declare that `hero` is an *input*.
We'll do it the way we *prefer*, by annotating the `hero` property with the `@Input` decorator that we imported earlier.
{@example 'toh-3/ts/src/app/hero-detail.component.ts' region='hero-input'}
Learn more about the `@Input()` decorator in the
[Attribute Directives](guide/attribute-directives) chapter.
## Refresh the AppModule
We return to the `AppModule`, the application's root module, and teach it to use the `HeroDetailComponent`.
We begin by importing the `HeroDetailComponent` so we can refer to it.
{@example 'toh-3/ts/src/app/app.module.ts' region='hero-detail-import'}
Then we add `HeroDetailComponent` to the `NgModule` decorator's `declarations` array.
This array contains the list of all components, pipes, and directives that we created
and that belong in our application's module.
{@example 'toh-3/ts/src/app/app.module.ts' region='declarations'}
## Refresh the AppComponentNow that the application knows about our `HeroDetailComponent`,
find the location in the `AppComponent` template where we removed the *Hero Detail* content
and add an element tag that represents the `HeroDetailComponent`.
<code-example language="html">
&lt;my-hero-detail>&lt;/my-hero-detail>
</code-example>
*my-hero-detail* is the name we set as the `selector` in the `HeroDetailComponent` metadata.The two components won't coordinate until we bind the `selectedHero` property of the `AppComponent`
to the `HeroDetailComponent` element's `hero` property like this:
<code-example language="html">
&lt;my-hero-detail [hero]="selectedHero">&lt;/my-hero-detail>
{@a selector}
To define a component, you always import the `Component` symbol.
The `@Component` decorator provides the Angular metadata for the component.
The CSS selector name, `hero-detail`, will match the element tag
that identifies this component within a parent component's template.
[Near the end of this tutorial page](tutorial/toh-pt3#add-hero-detail "Add the HeroDetailComponent to the AppComponent"),
you'll add a `<hero-detail>` element to the `AppComponent` template.
Always `export` the component class because you'll always `import` it elsewhere.### Hero detail template
To move the hero detail view to the `HeroDetailComponent`,
cut the hero detail _content_ from the bottom of the `AppComponent` template
and paste it into a new `template` property in the `@Component` metadata.
The `HeroDetailComponent` has a _hero_, not a _selected hero_.
Replace the word, "selectedHero", with the word, "hero", everywhere in the template.
When you're done, the new template should look like this:
<code-example path="toh-3/src/app/hero-detail.component.ts" region="template" linenums="false">
</code-example>
The `AppComponent`s template should now look like this
### Add the *hero* property
The `HeroDetailComponent` template binds to the component's `hero` property.
Add that property to the `HeroDetailComponent` class like this:
<code-example path="toh-3/app/hero-detail.component.1.ts" region="hero">
</code-example>
The `hero` property is typed as an instance of `Hero`.
The `Hero` class is still in the `app.component.ts` file.
Now there are two components that need to reference the `Hero` class.
The Angular [style guide](guide/style-guide) recommends one class per file anyway.
Move the `Hero` class from `app.component.ts` to its own `hero.ts` file.
{@example 'toh-3/ts/src/app/app.component.ts' region='hero-detail-template'}
<code-example path="toh-3/src/app/hero.ts" linenums="false">
Thanks to the binding, the `HeroDetailComponent` should receive the hero from the `AppComponent` and display that hero's detail beneath the list.
The detail should update every time the user picks a new hero.
### It works!
When we view our app in the browser we see the list of heroes.
When we select a hero we can see the selected heros details.
</code-example>
What's fundamentally new is that we can use this `HeroDetailComponent`
to show hero details anywhere in the app.
Now that the `Hero` class is in its own file, the `AppComponent` and the `HeroDetailComponent` have to import it.
Add the following `import` statement near the top of _both_ the `app.component.ts` and the `hero-detail.component.ts` files.
Weve created our first reusable component!
<code-example path="toh-3/app/hero-detail.component.1.ts" region="hero-import">
### Reviewing the App Structure
Lets verify that we have the following structure after all of our good refactoring in this chapter:
</code-example>
### The *hero* property is an *input* property
[Later in this page](tutorial/toh-pt3#add-hero-detail "Add the HeroDetailComponent to the AppComponent"),
the parent `AppComponent` will tell the child `HeroDetailComponent` which hero to display
by binding its `selectedHero` to the `hero` property of the `HeroDetailComponent`.
The binding will look like this:
<code-example path="toh-3/app/app.component.1.html" region="hero-detail-binding" linenums="false">
</code-example>
Putting square brackets around the `hero` property, to the left of the equal sign (=),
makes it the *target* of a property binding expression.
You must declare a *target* binding property to be an *input* property.
Otherwise, Angular rejects the binding and throws an error.
First, amend the `@angular/core` import statement to include the `Input` symbol.
<code-example path="toh-3/src/app/hero-detail.component.ts" region="import-input" linenums="false">
</code-example>
Then declare that `hero` is an *input* property by
preceding it with the `@Input` decorator that you imported earlier.
<code-example path="toh-3/src/app/hero-detail.component.ts" region="hero" linenums="false">
</code-example>
~~~ {.l-sub-section}
Read more about _input_ properties in the
[Attribute Directives](guide/attribute-directives) page.
~~~
That's it. The `hero` property is the only thing in the `HeroDetailComponent` class.
<code-example path="toh-3/src/app/hero-detail.component.ts" region="class" linenums="false">
</code-example>
All it does is receive a hero object through its `hero` input property and then bind to that property with its template.
Here's the complete `HeroDetailComponent`.
<code-example path="toh-3/src/app/hero-detail.component.ts">
</code-example>
## Declare _HeroDetailComponent_ in the _AppModule_
Every component must be declared in one&mdash;and only one&mdash;Angular module.
Open `app.module.ts` in your editor and import the `HeroDetailComponent` so you can refer to it.
<code-example path="toh-3/src/app/app.module.ts" region="hero-detail-import">
</code-example>
Add `HeroDetailComponent` to the module's `declarations` array.
<code-example path="toh-3/src/app/app.module.ts" region="declarations" linenums="false">
</code-example>
In general, the `declarations` array contains a list of application components, pipes, and directives that belong to the module.
A component must be declared in a module before other components can reference it.
This module declares only the two application components, `AppComponent` and `HeroDetailComponent`.
~~~ {.l-sub-section}
Read more about Angular modules in the [NgModules](guide/ngmodule) guide.
~~~
{@a add-hero-detail}
## Add the _HeroDetailComponent_ to the _AppComponent_The `AppComponent` is still a master/detail view.
It used to display the hero details on its own, before you cut out that portion of the template.
Now it will delegate to the `HeroDetailComponent`.
Recall that `hero-detail` is the CSS [`selector`](tutorial/toh-pt3#selector "HeroDetailComponent selector")
in the `HeroDetailComponent` metadata.
That's the tag name of the element that represents the `HeroDetailComponent`.
Add a `<hero-detail>` element near the bottom of the `AppComponent` template,
where the hero detail view used to be.
Coordinate the master `AppComponent` with the `HeroDetailComponent`
by binding the `selectedHero` property of the `AppComponent`
to the `hero` property of the `HeroDetailComponent`.
<code-example path="toh-3/app/app.component.1.html" region="hero-detail-binding" linenums="false">
</code-example>
Now every time the `selectedHero` changes, the `HeroDetailComponent` gets a new hero to display.
The revised `AppComponent` template should look like this:
<code-example path="toh-3/src/app/app.component.ts" region="hero-detail-template" linenums="false">
</code-example>
## What changed?
As [before](tutorial/toh-pt2), whenever a user clicks on a hero name,
the hero detail appears below the hero list.
But now the `HeroDetailView` is presenting those details.
Refactoring the original `AppComponent` into two components yields benefits, both now and in the future:
1. You simplified the `AppComponent` by reducing its responsibilities.
1. You can evolve the `HeroDetailComponent` into a rich hero editor
without touching the parent `AppComponent`.
1. You can evolve the `AppComponent` without touching the hero detail view.
1. You can re-use the `HeroDetailComponent` in the template of some future parent component.
### Review the app structure
Verify that you have the following structure:
<aio-filetree>
@ -316,50 +371,50 @@ Lets verify that we have the following structure after all of our good refact
</aio-filetree>
Here are the code files we discussed in this chapter.
<md-tab-group>
<md-tab label="src/app/hero-detail.component.ts">
{@example 'toh-3/ts/src/app/hero-detail.component.ts'}
</md-tab>
Here are the code files discussed in this page.
<md-tab label="src/app/app.component.ts">
{@example 'toh-3/ts/src/app/app.component.ts'}
</md-tab>
<code-tabs>
<code-pane title="src/app/hero-detail.component.ts" path="toh-3/src/app/hero-detail.component.ts">
</code-pane>
<md-tab label="src/app/hero.ts">
{@example 'toh-3/ts/src/app/hero.ts'}
</md-tab>
<code-pane title="src/app/app.component.ts" path="toh-3/src/app/app.component.ts">
</code-pane>
<md-tab label="src/app/app.module.ts">
{@example 'toh-3/ts/src/app/app.module.ts'}
</md-tab>
<code-pane title="src/app/hero.ts" path="toh-3/src/app/hero.ts">
</code-pane>
</md-tab-group>
<code-pane title="src/app/app.module.ts" path="toh-3/src/app/app.module.ts">
</code-pane>
## The Road Weve Travelled
Lets take stock of what weve built.
</code-tabs>
* We created a reusable component
* We learned how to make a component accept input
* We learned to declare the application directives we need in an Angular module. We
list the directives in the `NgModule` decorator's `declarations` array.
* We learned to bind a parent component to a child component.
Run the <live-example></live-example> for this part.
## The road youve travelled
Here's what you achieved in this page:
## The Road Ahead
Our Tour of Heroes has become more reusable with shared components.
* You created a reusable component.
* You learned how to make a component accept input.
* You learned to declare the required application directives in an Angular module. You
listed the directives in the `NgModule` decorator's `declarations` array.
* You learned to bind a parent component to a child component.
We're still getting our (mock) data within the `AppComponent`.
That's not sustainable.
We should refactor data access to a separate service
and share it among the components that need data.
Your app should look like this <live-example></live-example>.
Well learn to create services in the [next tutorial](tutorial/toh-pt4) chapter.
## The road ahead
The Tour of Heroes app is more reusable with shared components,
but its (mock) data is still hard coded within the `AppComponent`.
That's not sustainable.
Data access should be refactored to a separate service
and shared among the components that need data.
Youll learn to create services in the [next tutorial](tutorial/toh-pt4) page.

View File

@ -2,27 +2,25 @@
Services
@intro
We create a reusable service to manage our hero data calls.
Create a reusable service to manage the hero data calls.
@description
The Tour of Heroes is evolving and we anticipate adding more components in the near future.
As the Tour of Heroes app evolves, you'll add more components that need access to hero data.
Multiple components will need access to hero data and we don't want to copy and
paste the same code over and over.
Instead, we'll create a single reusable data service and learn to
inject it in the components that need it.
Instead of copying and pasting the same code over and over,
you'll create a single reusable data service and
inject it into the components that need it.
Using a separate service keeps components lean and focused on supporting the view,
and makes it easy to unit-test components with a mock service.
Refactoring data access to a separate service keeps the component lean and focused on supporting the view.
It also makes it easier to unit test the component with a mock service.
Because data services are invariably asynchronous,
you'll finish the page with a *!{_Promise}*-based version of the data service.
Because data services are invariably asynchronous,
we'll finish the chapter with a **!{_Promise}**-based version of the data service.
When you're done with this page, the app should look like this <live-example></live-example>.
Run the <live-example></live-example> for this part.
## Where We Left Off
Before we continue with our Tour of Heroes, lets verify we have the following structure.
If not, well need to go back and follow the previous chapters.
## Where you left off
Before continuing with the Tour of Heroes, verify that you have the following structure.
If not, go back to the previous pages.
<aio-filetree>
@ -98,242 +96,313 @@ If not, well need to go back and follow the previous chapters.
</aio-filetree>
### Keep the app transpiling and running
Open a terminal/console window.
Start the TypeScript compiler, watch for changes, and start our server by entering the command:
## Keep the app transpiling and running
Enter the following command in the terminal window:
<code-example language="sh" class="code-shell">
npm start
</code-example>
The application runs and updates automatically as we continue to build the Tour of Heroes.
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.
## Creating a Hero Service
Our stakeholders have shared their larger vision for our app.
They tell us they want to show the heroes in various ways on different pages.
We already can select a hero from a list.
Soon we'll add a dashboard with the top performing heroes and create a separate view for editing hero details.
All three views need hero data.
You can keep building the Tour of Heroes without pausing to recompile or refresh the browser.
At the moment the `AppComponent` defines mock heroes for display.
We have at least two objections.
First, defining heroes is not the component's job.
Second, we can't easily share that list of heroes with other components and views.
## Creating a hero service
The stakeholders want to show the heroes in various ways on different pages.
Users can already select a hero from a list.
Soon you'll add a dashboard with the top performing heroes and create a separate view for editing hero details.
All three views need hero data.
We can refactor this hero data acquisition business to a single service that provides heroes, and
share that service with all components that need heroes.
At the moment, the `AppComponent` defines mock heroes for display.
However, defining heroes is not the component's job,
and you can't easily share the list of heroes with other components and views.
In this page, you'll move the hero data acquisition business to a single service that provides the data and
share that service with all components that need the data.
### Create the HeroService
Create a file in the `app` folder called `hero.service.ts`.
We've adopted a convention in which we spell the name of a service in lowercase followed by `.service`.
If the service name were multi-word, we'd spell the base filename in lower [dash-case](guide/glossary).
The `SpecialSuperHeroService` would be defined in the `special-super-hero.service.ts` file.We name the class `HeroService` and export it for others to import.
Create a file in the `app` folder called `hero.service.ts`.
~~~ {.l-sub-section}
{@example 'toh-4/ts/src/app/hero.service.1.ts' region='empty-class'}
### Injectable Services
Notice that we imported the Angular `Injectable` function and applied that function as an `@Injectable()` decorator.
~~~ {.callout.is-helpful}
**Don't forget the parentheses!** Neglecting them leads to an error that's difficult to diagnose.
The naming convention for service files is the service name in lowercase followed by `.service`.
For a multi-word service name, use lower [dash-case](guide/glossary).
For example, the filename for `SpecialSuperHeroService` is `special-super-hero.service.ts`.
~~~
TypeScript sees the `@Injectable()` decorator and emits metadata about our service,
metadata that Angular may need to inject other dependencies into this service.
The `HeroService` doesn't have any dependencies *at the moment*. Add the decorator anyway.
It is a "best practice" to apply the `@Injectable()` decorator *from the start*
both for consistency and for future-proofing.
### Getting Heroes
Add a `getHeroes` method stub.
{@example 'toh-4/ts/src/app/hero.service.1.ts' region='getHeroes-stub'}
We're holding back on the implementation for a moment to make an important point.
The consumer of our service doesn't know how the service gets the data.
Our `HeroService` could get `Hero` data from anywhere.
It could get the data from a web service or local storage
or from a mock data source.
That's the beauty of removing data access from the component.
We can change our minds about the implementation as often as we like,
for whatever reason, without touching any of the components that need heroes.
Name the class `HeroService` and export it for others to import.
### Mock Heroes
We already have mock `Hero` data sitting in the `AppComponent`. It doesn't belong there. It doesn't belong *here* either.
We'll move the mock data to its own file.
<code-example path="toh-4/src/app/hero.service.1.ts" region="empty-class" linenums="false">
</code-example>
### Injectable services
Notice that you imported the Angular `Injectable` function and applied that function as an `@Injectable()` decorator.
~~~ {.callout.is-helpful}
Don't forget the parentheses. Omitting them leads to an error that's difficult to diagnose.
~~~
The `@Injectable()` decorator tells TypeScript to emit metadata about the service.
The metadata specifies that Angular may need to inject other dependencies into this service.
Although the `HeroService` doesn't have any dependencies at the moment,
applying the `@Injectable()` decorator from the start ensures
consistency and future-proofing.
### Getting hero data
Add a `getHeroes()` method stub.
<code-example path="toh-4/src/app/hero.service.1.ts" region="getHeroes-stub" linenums="false">
</code-example>
The `HeroService` could get `Hero` data from anywhere&mdash;a
web service, local storage, or a mock data source.
Removing data access from the component means
you can change your mind about the implementation anytime,
without touching the components that need hero data.
### Move the mock hero data
Cut the `HEROES` array from `app.component.ts` and paste it to a new file in the `app` folder named `mock-heroes.ts`.
We copy the `import {Hero} ...` statement as well because the heroes array uses the `Hero` class.
Additionally, copy the `import {Hero} ...` statement because the heroes array uses the `Hero` class.
{@example 'toh-4/ts/src/app/mock-heroes.ts'}
<code-example path="toh-4/src/app/mock-heroes.ts">
We export the `HEROES` constant so we can import it elsewhere &mdash; such as our `HeroService`.
</code-example>
Meanwhile, back in `app.component.ts` where we cut away the `HEROES` array,
we leave behind an uninitialized `heroes` property:
The `HEROES` constant is exported so it can be imported elsewhere, such as the `HeroService`.
{@example 'toh-4/ts/src/app/app.component.1.ts' region='heroes-prop'}
In `app.component.ts`, where you cut the `HEROES` array,
add an uninitialized `heroes` property:
### Return Mocked Heroes
Back in the `HeroService` we import the mock `HEROES` and return it from the `getHeroes` method.
Our `HeroService` looks like this:
<code-example path="toh-4/src/app/app.component.1.ts" region="heroes-prop" linenums="false">
{@example 'toh-4/ts/src/app/hero.service.1.ts' region='full'}
</code-example>
### Use the Hero Service
We're ready to use the `HeroService` in other components starting with our `AppComponent`.
### Return mocked hero data
Back in the `HeroService`, import the mock `HEROES` and return it from the `getHeroes()` method.
The `HeroService` looks like this:
We begin, as usual, by importing the thing we want to use, the `HeroService`.Importing the service allows us to *reference* it in our code.
<code-example path="toh-4/src/app/hero.service.1.ts" region="full" linenums="false">
</code-example>
### Import the hero service
You're ready to use the `HeroService` in other components, starting with `AppComponent`.
Import the `HeroService` so that you can reference it in the code.
<code-example path="toh-4/src/app/app.component.ts" linenums="false" title="toh-4/ts/src/app/app.component.ts (hero-service-import)" region="hero-service-import">
</code-example>
### Don't use *new* with the *HeroService*
How should the `AppComponent` acquire a runtime concrete `HeroService` instance?
### Do we *new* the *HeroService*? No way!
We could create a new instance of the `HeroService` with `new` like this:
You could create a new instance of the `HeroService` with `new` like this:
{@example 'toh-4/ts/src/app/app.component.1.ts' region='new-service'}
<code-example path="toh-4/src/app/app.component.1.ts" region="new-service" linenums="false">
That's a bad idea for several reasons including
</code-example>
* Our component has to know how to create a `HeroService`.
If we ever change the `HeroService` constructor,
we'll have to find every place we create the service and fix it.
Running around patching code is error prone and adds to the test burden.
* We create a new service each time we use `new`.
What if the service should cache heroes and share that cache with others?
We couldn't do that.
However, this option isn't ideal for the following reasons:
* We're locking the `AppComponent` into a specific implementation of the `HeroService`.
It will be hard to switch implementations for different scenarios.
Can we operate offline?
Will we need different mocked versions under test?
Not easy.
* The component has to know how to create a `HeroService`.
If you change the `HeroService` constructor,
you must find and update every place you created the service.
Patching code in multiple places is error prone and adds to the test burden.
* You create a service each time you use `new`.
What if the service caches heroes and shares that cache with others?
You couldn't do that.
* With the `AppComponent` locked into a specific implementation of the `HeroService`,
switching implementations for different scenarios, such as operating offline or using
different mocked versions for testing, would be difficult.
*What if ... what if ... Hey, we've got work to do!*
We get it. Really we do.
But it is so ridiculously easy to avoid these problems that there is no excuse for doing it wrong.
### Inject the *HeroService*
Two lines replace the one line that created with *new*:
1. We add a constructor that also defines a private property.
1. We add to the component's `providers` metadata.
Instead of using the *new* line, you'll add two lines.
* Add a constructor that also defines a private property.
* Add to the component's `providers` metadata.
Here's the constructor:
Add the constructor:
{@example 'toh-4/ts/src/app/app.component.1.ts' region='ctor'}
<code-example path="toh-4/src/app/app.component.1.ts" region="ctor">
The constructor itself does nothing. The parameter simultaneously
defines a private `heroService` property and identifies it as a `HeroService` injection site.Now Angular will know to supply an instance of the `HeroService` when it creates a new `AppComponent`.
</code-example>
Learn more about Dependency Injection in the [Dependency Injection](guide/dependency-injection) chapter.The *injector* does not know yet how to create a `HeroService`.
If we ran our code now, Angular would fail with an error:
The constructor itself does nothing. The parameter simultaneously
defines a private `heroService` property and identifies it as a `HeroService` injection site.Now Angular knows to supply an instance of the `HeroService` when it creates an `AppComponent`.
~~~ {.l-sub-section}
Read more about dependency injection in the [Dependency Injection](guide/dependency-injection) page.
~~~
The *injector* doesn't know yet how to create a `HeroService`.
If you ran the code now, Angular would fail with this error:
<code-example format="nocode">
EXCEPTION: No provider for HeroService! (AppComponent -> HeroService)
</code-example>
We have to teach the *injector* how to make a `HeroService` by registering a `HeroService` **provider**.
Do that by adding the following `providers` array property to the bottom of the component metadata
To teach the injector how to make a `HeroService`,
add the following `providers` array property to the bottom of the component metadata
in the `@Component` call.
The `providers` array tells Angular to create a fresh instance of the `HeroService` when it creates a new `AppComponent`.
The `AppComponent` can use that service to get heroes and so can every child component of its component tree.
<code-example path="toh-4/src/app/app.component.1.ts" linenums="false" title="toh-4/ts/src/app/app.component.ts (providers)" region="providers">
</code-example>
The `providers` array tells Angular to create a fresh instance of the `HeroService` when it creates an `AppComponent`.
The `AppComponent`, as well as its child components, can use that service to get hero data.
{@a child-component}
### *getHeroes* in the *AppComponent*
We've got the service in a `heroService` private variable. Let's use it.
### *getHeroes()* in the *AppComponent*
The service is in a `heroService` private variable.
We pause to think. We can call the service and get the data in one line.
You could call the service and get the data in one line.
{@example 'toh-4/ts/src/app/app.component.1.ts' region='get-heroes'}
<code-example path="toh-4/src/app/app.component.1.ts" region="get-heroes" linenums="false">
We don't really need a dedicated method to wrap one line. We write it anyway:<a id="oninit"></a>### The *ngOnInit* Lifecycle Hook
`AppComponent` should fetch and display heroes without a fuss.
Where do we call the `getHeroes` method? In a constructor? We do *not*!
</code-example>
Years of experience and bitter tears have taught us to keep complex logic out of the constructor,
especially anything that might call a server as a data access method is sure to do.
You don't really need a dedicated method to wrap one line. Write it anyway:
The constructor is for simple initializations like wiring constructor parameters to properties.
It's not for heavy lifting. We should be able to create a component in a test and not worry that it
might do real work &mdash; like calling a server! &mdash; before we tell it to do so.
If not the constructor, something has to call `getHeroes`.
<code-example path="toh-4/src/app/app.component.1.ts" linenums="false" title="toh-4/ts/src/app/app.component.ts (getHeroes)" region="getHeroes">
Angular will call it if we implement the Angular **ngOnInit** *Lifecycle Hook*.
Angular offers a number of interfaces for tapping into critical moments in the component lifecycle:
</code-example>
<a id="oninit"></a>### The *ngOnInit* lifecycle hook
`AppComponent` should fetch and display hero data with no issues.
You might be tempted to call the `getHeroes()` method in a constructor, but
a constructor should not contain complex logic,
especially a constructor that calls a server, such as as a data access method.
The constructor is for simple initializations, like wiring constructor parameters to properties.
To have Angular call `getHeroes()`, you can implement the Angular *ngOnInit lifecycle hook*.
Angular offers interfaces for tapping into critical moments in the component lifecycle:
at creation, after each change, and at its eventual destruction.
Each interface has a single method. When the component implements that method, Angular calls it at the appropriate time.
Learn more about lifecycle hooks in the [Lifecycle Hooks](guide/lifecycle-hooks) chapter.Here's the essential outline for the `OnInit` interface:
{@example 'toh-4/ts/src/app/app.component.1.ts' region='on-init'}
~~~ {.l-sub-section}
We write an `ngOnInit` method with our initialization logic inside and leave it to Angular to call it
at the right time. In our case, we initialize by calling `getHeroes`.Our application should be running as expected, showing a list of heroes and a hero detail view
when we click on a hero name.
Read more about lifecycle hooks in the [Lifecycle Hooks](guide/lifecycle-hooks) page.
We're getting closer. But something isn't quite right.
~~~
<a id="async"></a>
## Async Services and !{_Promise}s
Our `HeroService` returns a list of mock heroes immediately.
Its `getHeroes` signature is synchronous
Here's the essential outline for the `OnInit` interface (don't copy this into your code):
{@example 'toh-4/ts/src/app/app.component.1.ts' region='get-heroes'}
<code-example path="toh-4/src/app/app.component.1.ts" region="on-init" linenums="false">
Ask for heroes and they are there in the returned result.
Someday we're going to get heroes from a remote server. We dont call http yet, but we aspire to in later chapters.
</code-example>
When we do, we'll have to wait for the server to respond and we won't be able to block the UI while we wait,
even if we want to (which we shouldn't) because the browser won't block.
Add the implementation for the `OnInit` interface to your export statement:
<code-example format="nocode">
export class AppComponent implements OnInit {}
</code-example>
We'll have to use some kind of asynchronous technique and that will change the signature of our `getHeroes` method.
Write an `ngOnInit` method with the initialization logic inside. Angular will call it
at the right time. In this case, initialize by calling `getHeroes()`.
We'll use *!{_Promise}s*.
<code-example path="toh-4/src/app/app.component.1.ts" linenums="false" title="toh-4/ts/src/app/app.component.ts (ng-on-init)" region="ng-on-init">
### The Hero Service makes a !{_Promise}
</code-example>
A **!{_Promise}** is ... well it's a promise to call us back later when the results are ready.
We ask an asynchronous service to do some work and give it a callback function.
It does that work (somewhere) and eventually it calls our function with the results of the work or an error.
We are simplifying. Learn about ES2015 Promises [here](http://exploringjs.com/es6/ch_promises.html) and elsewhere on the web.Update the `HeroService` with this !{_Promise}-returning `getHeroes` method:
The app should run as expected, showing a list of heroes and a hero detail view
when you click on a hero name.
<a id="async"></a>## Async services and !{_Promise}s
The `HeroService` returns a list of mock heroes immediately;
its `getHeroes()` signature is synchronous.
{@example 'toh-4/ts/src/app/hero.service.ts' region='get-heroes'}
<code-example path="toh-4/src/app/app.component.1.ts" region="get-heroes" linenums="false">
We're still mocking the data. We're simulating the behavior of an ultra-fast, zero-latency server,
by returning an **immediately resolved !{_Promise}** with our mock heroes as the result.
</code-example>
Eventually, the hero data will come from a remote server.
When using a remote server, users don't have to wait for the server to respond;
additionally, you aren't able to block the UI during the wait.
To coordinate the view with the response,
you can use *!{_Promise}s*, which is an asynchronous
technique that changes the signature of the `getHeroes()` method.
### The hero service makes a !{_Promise}
A *!{_Promise}* essentially promises to call back when the results are ready.
You ask an asynchronous service to do some work and give it a callback function.
The service does that work and eventually calls the function with the results or an error.
~~~ {.l-sub-section}
This is a simplified explanation. Read more about ES2015 Promises in the
[Promises for asynchronous programming](http://exploringjs.com/es6/ch_promises.html) page of
[Exploring ES6](http://http://exploringjs.com/es6.html).
~~~
Update the `HeroService` with this !{_Promise}-returning `getHeroes()` method:
<code-example path="toh-4/src/app/hero.service.ts" region="get-heroes" linenums="false">
</code-example>
You're still mocking the data. You're simulating the behavior of an ultra-fast, zero-latency server,
by returning an *immediately resolved !{_Promise}* with the mock heroes as the result.
### Act on the !{_Promise}
Returning to the `AppComponent` and its `getHeroes` method, we see that it still looks like this:
{@example 'toh-4/ts/src/app/app.component.1.ts' region='getHeroes'}
As a result of the change to `HeroService`, `this.heroes` is now set to a !{_Promise} rather than an array of heroes.
As a result of our change to `HeroService`, we're now setting `this.heroes` to a !{_Promise} rather than an array of heroes.
<code-example path="toh-4/src/app/app.component.1.ts" region="getHeroes" linenums="false">
We have to change our implementation to *act on the !{_Promise} when it resolves*.
When the !{_Promise} resolves successfully, *then* we will have heroes to display.
</code-example>
We pass our callback function as an argument to the !{_Promise}'s **then** method:
You have to change the implementation to *act on the !{_Promise} when it resolves*.
When the !{_Promise} resolves successfully, you'll have heroes to display.
{@example 'toh-4/ts/src/app/app.component.ts' region='get-heroes'}
Pass the callback function as an argument to the !{_Promise}'s `then()` method:
<code-example path="toh-4/src/app/app.component.ts" region="get-heroes" linenums="false">
</code-example>
The [ES2015 arrow function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions)
in the callback is more succinct than the equivalent function expression and gracefully handles *this*.Our callback sets the component's `heroes` property to the array of heroes returned by the service. That's all there is to it!
Our app should still be running, still showing a list of heroes, and still
~~~ {.l-sub-section}
As described in [Arrow functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions),
the ES2015 arrow function
in the callback is more succinct than the equivalent function expression and gracefully handles `this`.
~~~
The callback sets the component's `heroes` property to the array of heroes returned by the service.
The app is still running, showing a list of heroes, and
responding to a name selection with a detail view.
Checkout the "[Take it slow](tutorial/toh-pt4#slow)" appendix to see what the app might be like with a poor connection.### Review the App Structure
Lets verify that we have the following structure after all of our good refactoring in this chapter:
~~~ {.l-sub-section}
At the end of this page, [Appendix: take it slow](tutorial/toh-pt4#slow) describes what the app might be like with a poor connection.
~~~
## Review the app structure
Verify that you have the following structure after all of your refactoring:
<aio-filetree>
@ -405,7 +474,7 @@ Lets verify that we have the following structure after all of our good refact
<aio-file>
node_modules ...
node_modules ...
</aio-file>
@ -419,55 +488,56 @@ Lets verify that we have the following structure after all of our good refact
</aio-filetree>
Here are the code files we discussed in this chapter.
<md-tab-group>
<md-tab label="src/app/hero.service.ts">
{@example 'toh-4/ts/src/app/hero.service.ts'}
</md-tab>
Here are the code files discussed in this page.
<md-tab label="src/app/app.component.ts">
{@example 'toh-4/ts/src/app/app.component.ts'}
</md-tab>
<code-tabs>
<code-pane title="src/app/hero.service.ts" path="toh-4/src/app/hero.service.ts">
</code-pane>
<md-tab label="src/app/mock-heroes.ts">
{@example 'toh-4/ts/src/app/mock-heroes.ts'}
</md-tab>
<code-pane title="src/app/app.component.ts" path="toh-4/src/app/app.component.ts">
</code-pane>
</md-tab-group>
<code-pane title="src/app/mock-heroes.ts" path="toh-4/src/app/mock-heroes.ts">
## The Road Weve Travelled
Lets take stock of what weve built.
</code-pane>
* We created a service class that can be shared by many components.
* We used the `ngOnInit` Lifecycle Hook to get our heroes when our `AppComponent` activates.
* We defined our `HeroService` as a provider for our `AppComponent`.
* We created mock hero data and imported them into our service.
* We designed our service to return a !{_Promise} and our component to get our data from the !{_Promise}.
Run the <live-example></live-example> for this part.
</code-tabs>
### The Road Ahead
Our Tour of Heroes has become more reusable using shared components and services.
We want to create a dashboard, add menu links that route between the views, and format data in a template.
As our app evolves, well learn how to design it to make it easier to grow and maintain.
## The road you've travelled
Here's what you achieved in this page:
We learn about Angular Component Router and navigation among the views in the [next tutorial](tutorial/toh-pt5) chapter.
* You created a service class that can be shared by many components.
* You used the `ngOnInit` lifecycle hook to get the hero data when the `AppComponent` activates.
* You defined the `HeroService` as a provider for the `AppComponent`.
* You created mock hero data and imported them into the service.
* You designed the service to return a !{_Promise} and the component to get the data from the !{_Promise}.
<a id="slow"></a>### Appendix: Take it slow
Your app should look like this <live-example></live-example>.
We can simulate a slow connection.
## The road ahead
The Tour of Heroes has become more reusable using shared components and services.
The next goal is to create a dashboard, add menu links that route between the views, and format data in a template.
As the app evolves, you'll discover how to design it to make it easier to grow and maintain.
Import the `Hero` symbol and add the following `getHeroesSlowly` method to the `HeroService`
Read about the Angular component router and navigation among the views in the [next tutorial](tutorial/toh-pt5) page.
{@example 'toh-4/ts/src/app/hero.service.ts' region='get-heroes-slowly'}
<a id="slow"></a>## Appendix: Take it slow
To simulate a slow connection,
import the `Hero` symbol and add the following `getHeroesSlowly()` method to the `HeroService`.
Like `getHeroes`, it also returns a !{_Promise}.
But this !{_Promise} waits 2 seconds before resolving the !{_Promise} with mock heroes.
<code-example path="toh-4/src/app/hero.service.ts" region="get-heroes-slowly" linenums="false">
Back in the `AppComponent`, replace `heroService.getHeroes` with `heroService.getHeroesSlowly`
</code-example>
Like `getHeroes()`, it also returns a !{_Promise}.
But this !{_Promise} waits two seconds before resolving the !{_Promise} with mock heroes.
Back in the `AppComponent`, replace `getHeroes()` with `getHeroesSlowly()`
and see how the app behaves.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff