2017-02-22 13:09:39 -05:00
|
|
|
|
@title
|
|
|
|
|
Services
|
|
|
|
|
|
|
|
|
|
@intro
|
2017-03-27 11:08:53 -04:00
|
|
|
|
Create a reusable service to manage the hero data calls.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
|
|
|
|
@description
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
As the Tour of Heroes app evolves, you'll add more components that need access to hero data.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
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.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
Because data services are invariably asynchronous,
|
2017-03-31 07:23:16 -04:00
|
|
|
|
you'll finish the page with a *Promise*-based version of the data service.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
When you're done with this page, the app should look like this <live-example></live-example>.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
## 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.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-30 15:04:18 -04:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
<div class='filetree'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
angular-tour-of-heroes
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class='children'>
|
|
|
|
|
|
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
src
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class='children'>
|
|
|
|
|
|
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
app
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class='children'>
|
|
|
|
|
|
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
app.component.ts
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
app.module.ts
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
hero.ts
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
hero-detail.component.ts
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
main.ts
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
index.html
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
styles.css
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
systemjs.config.js
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
tsconfig.json
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
node_modules ...
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
package.json
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
## Keep the app transpiling and running
|
|
|
|
|
Enter the following command in the terminal window:
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-30 15:04:18 -04:00
|
|
|
|
|
2017-02-22 13:09:39 -05:00
|
|
|
|
<code-example language="sh" class="code-shell">
|
2017-03-31 19:57:13 -04:00
|
|
|
|
npm start
|
|
|
|
|
|
2017-02-22 13:09:39 -05:00
|
|
|
|
</code-example>
|
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
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.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
You can keep building the Tour of Heroes without pausing to recompile or refresh the browser.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
## 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.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
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.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
|
|
|
|
### Create the HeroService
|
2017-03-27 11:08:53 -04:00
|
|
|
|
Create a file in the `app` folder called `hero.service.ts`.
|
|
|
|
|
|
2017-04-10 11:51:13 -04:00
|
|
|
|
<div class="l-sub-section">
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
The naming convention for service files is the service name in lowercase followed by `.service`.
|
2017-07-24 14:19:34 -04:00
|
|
|
|
For a multi-word service name, use lower [dash-case](guide/glossary#dash-case).
|
2017-03-27 11:08:53 -04:00
|
|
|
|
For example, the filename for `SpecialSuperHeroService` is `special-super-hero.service.ts`.
|
|
|
|
|
|
2017-04-10 11:51:13 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
Name the class `HeroService` and export it for others to import.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
|
|
|
|
|
2017-04-18 14:16:02 -04:00
|
|
|
|
<code-example path="toh-pt4/src/app/hero.service.1.ts" region="empty-class" title="src/app/hero.service.ts (starting point)" linenums="false">
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
|
|
|
|
</code-example>
|
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
### Injectable services
|
|
|
|
|
Notice that you imported the Angular `Injectable` function and applied that function as an `@Injectable()` decorator.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-10 11:51:13 -04:00
|
|
|
|
<div class="callout is-helpful">
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
Don't forget the parentheses. Omitting them leads to an error that's difficult to diagnose.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-10 11:51:13 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
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.
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
### Getting hero data
|
|
|
|
|
Add a `getHeroes()` method stub.
|
|
|
|
|
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-18 14:16:02 -04:00
|
|
|
|
<code-example path="toh-pt4/src/app/hero.service.1.ts" region="getHeroes-stub" title="src/app/hero.service.ts (getHeroes stub)" linenums="false">
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
</code-example>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
The `HeroService` could get `Hero` data from anywhere—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.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
### 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`.
|
|
|
|
|
Additionally, copy the `import {Hero} ...` statement because the heroes array uses the `Hero` class.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
|
|
|
|
|
2017-04-18 14:16:02 -04:00
|
|
|
|
<code-example path="toh-pt4/src/app/mock-heroes.ts" title="src/app/mock-heroes.ts">
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
</code-example>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
The `HEROES` constant is exported so it can be imported elsewhere, such as the `HeroService`.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
In `app.component.ts`, where you cut the `HEROES` array,
|
|
|
|
|
add an uninitialized `heroes` property:
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-18 14:16:02 -04:00
|
|
|
|
<code-example path="toh-pt4/src/app/app.component.1.ts" region="heroes-prop" title="src/app/app.component.ts (heroes property)" linenums="false">
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
</code-example>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
### Return mocked hero data
|
|
|
|
|
Back in the `HeroService`, import the mock `HEROES` and return it from the `getHeroes()` method.
|
|
|
|
|
The `HeroService` looks like this:
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-18 14:16:02 -04:00
|
|
|
|
<code-example path="toh-pt4/src/app/hero.service.1.ts" region="full" title="src/app/hero.service.ts" linenums="false">
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
</code-example>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
### Import the hero service
|
|
|
|
|
You're ready to use the `HeroService` in other components, starting with `AppComponent`.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
Import the `HeroService` so that you can reference it in the code.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-18 14:16:02 -04:00
|
|
|
|
<code-example path="toh-pt4/src/app/app.component.ts" linenums="false" title="src/app/app.component.ts (hero-service-import)" region="hero-service-import">
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
</code-example>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
### Don't use *new* with the *HeroService*
|
|
|
|
|
How should the `AppComponent` acquire a runtime concrete `HeroService` instance?
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
You could create a new instance of the `HeroService` with `new` like this:
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-18 14:16:02 -04:00
|
|
|
|
<code-example path="toh-pt4/src/app/app.component.1.ts" region="new-service" title="src/app/app.component.ts" linenums="false">
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
</code-example>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
However, this option isn't ideal for the following reasons:
|
|
|
|
|
|
|
|
|
|
* 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.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Inject the *HeroService*
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
Instead of using the *new* line, you'll add two lines.
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
* Add a constructor that also defines a private property.
|
|
|
|
|
* Add to the component's `providers` metadata.
|
|
|
|
|
|
|
|
|
|
Add the constructor:
|
|
|
|
|
|
2017-04-18 14:16:02 -04:00
|
|
|
|
<code-example path="toh-pt4/src/app/app.component.1.ts" region="ctor" title="src/app/app.component.ts (constructor)">
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
|
|
|
|
</code-example>
|
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
The constructor itself does nothing. The parameter simultaneously
|
2017-03-31 19:57:13 -04:00
|
|
|
|
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`.
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-10 11:51:13 -04:00
|
|
|
|
<div class="l-sub-section">
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
Read more about dependency injection in the [Dependency Injection](guide/dependency-injection) page.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-10 11:51:13 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
The *injector* doesn't know yet how to create a `HeroService`.
|
|
|
|
|
If you ran the code now, Angular would fail with this error:
|
2017-03-30 15:04:18 -04:00
|
|
|
|
|
2017-02-22 13:09:39 -05:00
|
|
|
|
<code-example format="nocode">
|
|
|
|
|
EXCEPTION: No provider for HeroService! (AppComponent -> HeroService)
|
|
|
|
|
</code-example>
|
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
To teach the injector how to make a `HeroService`,
|
|
|
|
|
add the following `providers` array property to the bottom of the component metadata
|
2017-02-22 13:09:39 -05:00
|
|
|
|
in the `@Component` call.
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-04-18 14:16:02 -04:00
|
|
|
|
<code-example path="toh-pt4/src/app/app.component.1.ts" linenums="false" title="src/app/app.component.ts (providers)" region="providers">
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
|
|
|
|
</code-example>
|
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
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.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
|
|
|
|
{@a child-component}
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
### *getHeroes()* in the *AppComponent*
|
|
|
|
|
The service is in a `heroService` private variable.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
You could call the service and get the data in one line.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-18 14:16:02 -04:00
|
|
|
|
<code-example path="toh-pt4/src/app/app.component.1.ts" region="get-heroes" title="src/app/app.component.ts" linenums="false">
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
</code-example>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
You don't really need a dedicated method to wrap one line. Write it anyway:
|
|
|
|
|
|
|
|
|
|
|
2017-04-18 14:16:02 -04:00
|
|
|
|
<code-example path="toh-pt4/src/app/app.component.1.ts" linenums="false" title="src/app/app.component.ts (getHeroes)" region="getHeroes">
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
|
|
|
|
</code-example>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
{@a oninit}
|
|
|
|
|
|
|
|
|
|
### The *ngOnInit* lifecycle hook
|
2017-03-27 11:08:53 -04:00
|
|
|
|
`AppComponent` should fetch and display hero data with no issues.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
You might be tempted to call the `getHeroes()` method in a constructor, but
|
|
|
|
|
a constructor should not contain complex logic,
|
2017-04-24 12:51:38 -04:00
|
|
|
|
especially a constructor that calls a server, such as a data access method.
|
2017-03-27 11:08:53 -04:00
|
|
|
|
The constructor is for simple initializations, like wiring constructor parameters to properties.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
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:
|
2017-02-22 13:09:39 -05:00
|
|
|
|
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.
|
|
|
|
|
|
2017-04-10 11:51:13 -04:00
|
|
|
|
<div class="l-sub-section">
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
Read more about lifecycle hooks in the [Lifecycle Hooks](guide/lifecycle-hooks) page.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-10 11:51:13 -04:00
|
|
|
|
</div>
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
Here's the essential outline for the `OnInit` interface (don't copy this into your code):
|
|
|
|
|
|
2017-04-18 14:16:02 -04:00
|
|
|
|
<code-example path="toh-pt4/src/app/app.component.1.ts" region="on-init" title="src/app/app.component.ts" linenums="false">
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
|
|
|
|
</code-example>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
Add the implementation for the `OnInit` interface to your export statement:
|
2017-03-30 15:04:18 -04:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
<code-example format="nocode">
|
|
|
|
|
export class AppComponent implements OnInit {}
|
|
|
|
|
</code-example>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
Write an `ngOnInit` method with the initialization logic inside. Angular will call it
|
|
|
|
|
at the right time. In this case, initialize by calling `getHeroes()`.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-18 14:16:02 -04:00
|
|
|
|
<code-example path="toh-pt4/src/app/app.component.1.ts" linenums="false" title="src/app/app.component.ts (ng-on-init)" region="ng-on-init">
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
</code-example>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
The app should run as expected, showing a list of heroes and a hero detail view
|
|
|
|
|
when you click on a hero name.
|
2017-03-31 19:57:13 -04:00
|
|
|
|
{@a async}
|
|
|
|
|
|
|
|
|
|
## Async services and Promises
|
2017-03-27 11:08:53 -04:00
|
|
|
|
The `HeroService` returns a list of mock heroes immediately;
|
|
|
|
|
its `getHeroes()` signature is synchronous.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-18 14:16:02 -04:00
|
|
|
|
<code-example path="toh-pt4/src/app/app.component.1.ts" region="get-heroes" title="src/app/app.component.ts" linenums="false">
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
|
|
|
|
</code-example>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
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.
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
To coordinate the view with the response,
|
2017-03-31 07:23:16 -04:00
|
|
|
|
you can use *Promises*, which is an asynchronous
|
2017-03-27 11:08:53 -04:00
|
|
|
|
technique that changes the signature of the `getHeroes()` method.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 07:23:16 -04:00
|
|
|
|
### The hero service makes a Promise
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 07:23:16 -04:00
|
|
|
|
A *Promise* essentially promises to call back when the results are ready.
|
2017-03-27 11:08:53 -04:00
|
|
|
|
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.
|
|
|
|
|
|
2017-04-10 11:51:13 -04:00
|
|
|
|
<div class="l-sub-section">
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
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
|
2017-06-22 02:46:13 -04:00
|
|
|
|
[Exploring ES6](http://exploringjs.com/es6.html).
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
|
|
|
|
|
2017-04-10 11:51:13 -04:00
|
|
|
|
</div>
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-31 07:23:16 -04:00
|
|
|
|
Update the `HeroService` with this Promise-returning `getHeroes()` method:
|
2017-03-30 15:04:18 -04:00
|
|
|
|
|
2017-04-18 14:16:02 -04:00
|
|
|
|
<code-example path="toh-pt4/src/app/hero.service.ts" region="get-heroes" title="src/app/hero.service.ts (excerpt)" linenums="false">
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
|
|
|
|
</code-example>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
You're still mocking the data. You're simulating the behavior of an ultra-fast, zero-latency server,
|
2017-03-31 07:23:16 -04:00
|
|
|
|
by returning an *immediately resolved Promise* with the mock heroes as the result.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 07:23:16 -04:00
|
|
|
|
### Act on the Promise
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 07:23:16 -04:00
|
|
|
|
As a result of the change to `HeroService`, `this.heroes` is now set to a `Promise` rather than an array of heroes.
|
2017-03-30 15:04:18 -04:00
|
|
|
|
|
2017-04-18 14:16:02 -04:00
|
|
|
|
<code-example path="toh-pt4/src/app/app.component.1.ts" region="getHeroes" title="src/app/app.component.ts (getHeroes - old)" linenums="false">
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
|
|
|
|
</code-example>
|
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-31 07:23:16 -04:00
|
|
|
|
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.
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
2017-03-31 07:23:16 -04:00
|
|
|
|
Pass the callback function as an argument to the Promise's `then()` method:
|
2017-03-30 15:04:18 -04:00
|
|
|
|
|
2017-04-18 14:16:02 -04:00
|
|
|
|
<code-example path="toh-pt4/src/app/app.component.ts" region="get-heroes" title="src/app/app.component.ts (getHeroes - revised)" linenums="false">
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
|
|
|
|
</code-example>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-04-10 11:51:13 -04:00
|
|
|
|
<div class="l-sub-section">
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
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`.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-10 11:51:13 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
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
|
2017-02-22 13:09:39 -05:00
|
|
|
|
responding to a name selection with a detail view.
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
2017-04-10 11:51:13 -04:00
|
|
|
|
<div class="l-sub-section">
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
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.
|
|
|
|
|
|
2017-04-10 11:51:13 -04:00
|
|
|
|
</div>
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
## Review the app structure
|
|
|
|
|
Verify that you have the following structure after all of your refactoring:
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-30 15:04:18 -04:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
<div class='filetree'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
angular-tour-of-heroes
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class='children'>
|
|
|
|
|
|
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
src
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class='children'>
|
|
|
|
|
|
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
app
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class='children'>
|
|
|
|
|
|
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
app.component.ts
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
app.module.ts
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
hero.ts
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
hero-detail.component.ts
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
hero.service.ts
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
mock-heroes.ts
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
main.ts
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
index.html
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
styles.css
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
systemjs.config.js
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
tsconfig.json
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
<div class='file'>
|
2017-03-27 11:08:53 -04:00
|
|
|
|
node_modules ...
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
<div class='file'>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
package.json
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-11 16:44:52 -04:00
|
|
|
|
</div>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
Here are the code files discussed in this page.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
<code-tabs>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-18 14:16:02 -04:00
|
|
|
|
<code-pane title="src/app/hero.service.ts" path="toh-pt4/src/app/hero.service.ts">
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
</code-pane>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-18 14:16:02 -04:00
|
|
|
|
<code-pane title="src/app/app.component.ts" path="toh-pt4/src/app/app.component.ts">
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
</code-pane>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-04-18 14:16:02 -04:00
|
|
|
|
<code-pane title="src/app/mock-heroes.ts" path="toh-pt4/src/app/mock-heroes.ts">
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
</code-pane>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
</code-tabs>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-09-27 16:45:47 -04:00
|
|
|
|
## Summary
|
2017-03-27 11:08:53 -04:00
|
|
|
|
Here's what you achieved in this page:
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
* 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.
|
2017-03-31 07:23:16 -04:00
|
|
|
|
* You designed the service to return a Promise and the component to get the data from the Promise.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
Your app should look like this <live-example></live-example>.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-09-27 16:45:47 -04:00
|
|
|
|
## Next step
|
2017-03-27 11:08:53 -04:00
|
|
|
|
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.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-05-04 15:21:31 -04:00
|
|
|
|
Read about the Angular component router and navigation among the views in the [next tutorial](tutorial/toh-pt5 "Routing and Navigation") page.
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
{@a slow}
|
|
|
|
|
|
|
|
|
|
## Appendix: Take it slow
|
2017-03-27 11:08:53 -04:00
|
|
|
|
To simulate a slow connection,
|
|
|
|
|
import the `Hero` symbol and add the following `getHeroesSlowly()` method to the `HeroService`.
|
|
|
|
|
|
2017-04-18 14:16:02 -04:00
|
|
|
|
<code-example path="toh-pt4/src/app/hero.service.ts" region="get-heroes-slowly" title="app/hero.service.ts (getHeroesSlowly)" linenums="false">
|
2017-03-27 11:08:53 -04:00
|
|
|
|
|
|
|
|
|
</code-example>
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-31 19:57:13 -04:00
|
|
|
|
|
|
|
|
|
|
2017-03-31 07:23:16 -04:00
|
|
|
|
Like `getHeroes()`, it also returns a `Promise`.
|
|
|
|
|
But this Promise waits two seconds before resolving the Promise with mock heroes.
|
2017-02-22 13:09:39 -05:00
|
|
|
|
|
2017-03-27 11:08:53 -04:00
|
|
|
|
Back in the `AppComponent`, replace `getHeroes()` with `getHeroesSlowly()`
|
2017-04-18 14:16:02 -04:00
|
|
|
|
and see how the app behaves.
|