docs(tutorial): combines all 4 sections + revisions/updates to a.53, hides 3&4
closes #488
ToH History (oldest-to-latest):
----------------------------
created code example/snippets files for use with +makeExample, replace usage of "pre.prettyprint.lang-bash" with this: code-example(format="." language="bash").
fixed spelling errors in examples file path used by +makeExample
changed usage of "code-example" to "+makeExample"
adding code example/snippets files used in toh 1
fixed example file paths, replaced "pre.prettyprint.lang-bash" with "code-example. "
(docs) toh-pt3 initial state
created code examples for display in jade, starting conversion of Google doc and trying +makeExample rendering
all text copied from doc to jade, still some styling and formatting to perform
completed conversion and styling, moved toh3 example files to "tutorial" folder under _examples
created specific code example files for chapter toh 3 and re-pathed references in +makeExample
minor edit
docs) toh combined - initial combined commit
updated ToH for a.52
tons of changes, including de-kebab-ing, removed src folder, updated tsconfig too
fixing snippets using incorrect ending input tag
using inline html and css for the app.component.
ToH Part 1 Code: updated the imports, removed obsolete directive delcarations
ToH Code Part 1: updated to use imports, interface. will hit others soon
toh part 1: ngModel fix
toh part1: removed obsolete story that referred to how we used to have to import and declare all directives we used. yay!
ToH Part 1: updated to use `boot.ts` and `app.component.ts`. this affected the partials, snippets, and the story.
toh part 1: using `npm run go`
toh parts 1 -4: modified all places to use `npm run go`
toh part 1: refactor for jade
toh part 1: fixing the code samples
toh part 2: seeping through the story
toh part 2: fixing snippets.
toh part 2: replaced ngClass with class.selected
toh part 2: removed whitespace
toh part 2: added final state to the code
toh: fixing paths
toh part 4: fixing src/app path to app
toh part 3: fixing folder path
toh part 2: fixed typo
toh part 2: typo on ngModel
toh part 2: added ngif
toh part 2: removed old hero property. moved the details lower, where we need it
toh index: updated hero list image to show consistent styling as the other images here
QS spelling error (targes -> targets)
tweeks: space and ngIF
2015-11-15 21:04:43 -05:00
|
|
|
|
include ../../../../_includes/_util-fns
|
|
|
|
|
|
2015-12-14 23:05:13 -05:00
|
|
|
|
:marked
|
|
|
|
|
# Services
|
2015-12-16 21:47:02 -05:00
|
|
|
|
The Tour of Heroes is evolving and we anticipate adding more components in the near future.
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
we'll finish the chapter with a promise-based version of the data service.
|
|
|
|
|
|
|
|
|
|
:marked
|
|
|
|
|
[Run the live example for part 4](/resources/live-examples/toh-4/ts/plnkr.html)
|
docs(tutorial): combines all 4 sections + revisions/updates to a.53, hides 3&4
closes #488
ToH History (oldest-to-latest):
----------------------------
created code example/snippets files for use with +makeExample, replace usage of "pre.prettyprint.lang-bash" with this: code-example(format="." language="bash").
fixed spelling errors in examples file path used by +makeExample
changed usage of "code-example" to "+makeExample"
adding code example/snippets files used in toh 1
fixed example file paths, replaced "pre.prettyprint.lang-bash" with "code-example. "
(docs) toh-pt3 initial state
created code examples for display in jade, starting conversion of Google doc and trying +makeExample rendering
all text copied from doc to jade, still some styling and formatting to perform
completed conversion and styling, moved toh3 example files to "tutorial" folder under _examples
created specific code example files for chapter toh 3 and re-pathed references in +makeExample
minor edit
docs) toh combined - initial combined commit
updated ToH for a.52
tons of changes, including de-kebab-ing, removed src folder, updated tsconfig too
fixing snippets using incorrect ending input tag
using inline html and css for the app.component.
ToH Part 1 Code: updated the imports, removed obsolete directive delcarations
ToH Code Part 1: updated to use imports, interface. will hit others soon
toh part 1: ngModel fix
toh part1: removed obsolete story that referred to how we used to have to import and declare all directives we used. yay!
ToH Part 1: updated to use `boot.ts` and `app.component.ts`. this affected the partials, snippets, and the story.
toh part 1: using `npm run go`
toh parts 1 -4: modified all places to use `npm run go`
toh part 1: refactor for jade
toh part 1: fixing the code samples
toh part 2: seeping through the story
toh part 2: fixing snippets.
toh part 2: replaced ngClass with class.selected
toh part 2: removed whitespace
toh part 2: added final state to the code
toh: fixing paths
toh part 4: fixing src/app path to app
toh part 3: fixing folder path
toh part 2: fixed typo
toh part 2: typo on ngModel
toh part 2: added ngif
toh part 2: removed old hero property. moved the details lower, where we need it
toh index: updated hero list image to show consistent styling as the other images here
QS spelling error (targes -> targets)
tweeks: space and ngIF
2015-11-15 21:04:43 -05:00
|
|
|
|
|
2015-12-14 23:05:13 -05:00
|
|
|
|
.l-main-section
|
|
|
|
|
:marked
|
2015-12-16 21:47:02 -05:00
|
|
|
|
## Where We Left Off
|
|
|
|
|
Before we continue with our Tour of Heroes, let’s verify we have the following structure.
|
|
|
|
|
If not, we’ll need to go back and follow the previous chapters.
|
docs(tutorial): combines all 4 sections + revisions/updates to a.53, hides 3&4
closes #488
ToH History (oldest-to-latest):
----------------------------
created code example/snippets files for use with +makeExample, replace usage of "pre.prettyprint.lang-bash" with this: code-example(format="." language="bash").
fixed spelling errors in examples file path used by +makeExample
changed usage of "code-example" to "+makeExample"
adding code example/snippets files used in toh 1
fixed example file paths, replaced "pre.prettyprint.lang-bash" with "code-example. "
(docs) toh-pt3 initial state
created code examples for display in jade, starting conversion of Google doc and trying +makeExample rendering
all text copied from doc to jade, still some styling and formatting to perform
completed conversion and styling, moved toh3 example files to "tutorial" folder under _examples
created specific code example files for chapter toh 3 and re-pathed references in +makeExample
minor edit
docs) toh combined - initial combined commit
updated ToH for a.52
tons of changes, including de-kebab-ing, removed src folder, updated tsconfig too
fixing snippets using incorrect ending input tag
using inline html and css for the app.component.
ToH Part 1 Code: updated the imports, removed obsolete directive delcarations
ToH Code Part 1: updated to use imports, interface. will hit others soon
toh part 1: ngModel fix
toh part1: removed obsolete story that referred to how we used to have to import and declare all directives we used. yay!
ToH Part 1: updated to use `boot.ts` and `app.component.ts`. this affected the partials, snippets, and the story.
toh part 1: using `npm run go`
toh parts 1 -4: modified all places to use `npm run go`
toh part 1: refactor for jade
toh part 1: fixing the code samples
toh part 2: seeping through the story
toh part 2: fixing snippets.
toh part 2: replaced ngClass with class.selected
toh part 2: removed whitespace
toh part 2: added final state to the code
toh: fixing paths
toh part 4: fixing src/app path to app
toh part 3: fixing folder path
toh part 2: fixed typo
toh part 2: typo on ngModel
toh part 2: added ngif
toh part 2: removed old hero property. moved the details lower, where we need it
toh index: updated hero list image to show consistent styling as the other images here
QS spelling error (targes -> targets)
tweeks: space and ngIF
2015-11-15 21:04:43 -05:00
|
|
|
|
|
2015-12-14 23:05:13 -05:00
|
|
|
|
.filetree
|
|
|
|
|
.file angular2-tour-of-heroes
|
|
|
|
|
.children
|
|
|
|
|
.file node_modules
|
|
|
|
|
.file app
|
|
|
|
|
.children
|
|
|
|
|
.file app.component.ts
|
2015-12-16 21:47:02 -05:00
|
|
|
|
.file hero.ts
|
|
|
|
|
.file hero-detail.component.ts
|
2016-01-28 19:15:26 -05:00
|
|
|
|
.file main.ts
|
2015-12-14 23:05:13 -05:00
|
|
|
|
.file index.html
|
|
|
|
|
.file package.json
|
|
|
|
|
.file tsconfig.json
|
|
|
|
|
:marked
|
|
|
|
|
### Keep the app transpiling and running
|
2015-12-16 21:47:02 -05:00
|
|
|
|
Open a terminal/console window.
|
|
|
|
|
Start the TypeScript compiler, watch for changes, and start our server by entering the command:
|
docs(tutorial): combines all 4 sections + revisions/updates to a.53, hides 3&4
closes #488
ToH History (oldest-to-latest):
----------------------------
created code example/snippets files for use with +makeExample, replace usage of "pre.prettyprint.lang-bash" with this: code-example(format="." language="bash").
fixed spelling errors in examples file path used by +makeExample
changed usage of "code-example" to "+makeExample"
adding code example/snippets files used in toh 1
fixed example file paths, replaced "pre.prettyprint.lang-bash" with "code-example. "
(docs) toh-pt3 initial state
created code examples for display in jade, starting conversion of Google doc and trying +makeExample rendering
all text copied from doc to jade, still some styling and formatting to perform
completed conversion and styling, moved toh3 example files to "tutorial" folder under _examples
created specific code example files for chapter toh 3 and re-pathed references in +makeExample
minor edit
docs) toh combined - initial combined commit
updated ToH for a.52
tons of changes, including de-kebab-ing, removed src folder, updated tsconfig too
fixing snippets using incorrect ending input tag
using inline html and css for the app.component.
ToH Part 1 Code: updated the imports, removed obsolete directive delcarations
ToH Code Part 1: updated to use imports, interface. will hit others soon
toh part 1: ngModel fix
toh part1: removed obsolete story that referred to how we used to have to import and declare all directives we used. yay!
ToH Part 1: updated to use `boot.ts` and `app.component.ts`. this affected the partials, snippets, and the story.
toh part 1: using `npm run go`
toh parts 1 -4: modified all places to use `npm run go`
toh part 1: refactor for jade
toh part 1: fixing the code samples
toh part 2: seeping through the story
toh part 2: fixing snippets.
toh part 2: replaced ngClass with class.selected
toh part 2: removed whitespace
toh part 2: added final state to the code
toh: fixing paths
toh part 4: fixing src/app path to app
toh part 3: fixing folder path
toh part 2: fixed typo
toh part 2: typo on ngModel
toh part 2: added ngif
toh part 2: removed old hero property. moved the details lower, where we need it
toh index: updated hero list image to show consistent styling as the other images here
QS spelling error (targes -> targets)
tweeks: space and ngIF
2015-11-15 21:04:43 -05:00
|
|
|
|
|
2015-12-14 23:05:13 -05:00
|
|
|
|
code-example(format="." language="bash").
|
2015-12-16 21:47:02 -05:00
|
|
|
|
npm start
|
docs(tutorial): combines all 4 sections + revisions/updates to a.53, hides 3&4
closes #488
ToH History (oldest-to-latest):
----------------------------
created code example/snippets files for use with +makeExample, replace usage of "pre.prettyprint.lang-bash" with this: code-example(format="." language="bash").
fixed spelling errors in examples file path used by +makeExample
changed usage of "code-example" to "+makeExample"
adding code example/snippets files used in toh 1
fixed example file paths, replaced "pre.prettyprint.lang-bash" with "code-example. "
(docs) toh-pt3 initial state
created code examples for display in jade, starting conversion of Google doc and trying +makeExample rendering
all text copied from doc to jade, still some styling and formatting to perform
completed conversion and styling, moved toh3 example files to "tutorial" folder under _examples
created specific code example files for chapter toh 3 and re-pathed references in +makeExample
minor edit
docs) toh combined - initial combined commit
updated ToH for a.52
tons of changes, including de-kebab-ing, removed src folder, updated tsconfig too
fixing snippets using incorrect ending input tag
using inline html and css for the app.component.
ToH Part 1 Code: updated the imports, removed obsolete directive delcarations
ToH Code Part 1: updated to use imports, interface. will hit others soon
toh part 1: ngModel fix
toh part1: removed obsolete story that referred to how we used to have to import and declare all directives we used. yay!
ToH Part 1: updated to use `boot.ts` and `app.component.ts`. this affected the partials, snippets, and the story.
toh part 1: using `npm run go`
toh parts 1 -4: modified all places to use `npm run go`
toh part 1: refactor for jade
toh part 1: fixing the code samples
toh part 2: seeping through the story
toh part 2: fixing snippets.
toh part 2: replaced ngClass with class.selected
toh part 2: removed whitespace
toh part 2: added final state to the code
toh: fixing paths
toh part 4: fixing src/app path to app
toh part 3: fixing folder path
toh part 2: fixed typo
toh part 2: typo on ngModel
toh part 2: added ngif
toh part 2: removed old hero property. moved the details lower, where we need it
toh index: updated hero list image to show consistent styling as the other images here
QS spelling error (targes -> targets)
tweeks: space and ngIF
2015-11-15 21:04:43 -05:00
|
|
|
|
|
2015-12-14 23:05:13 -05:00
|
|
|
|
:marked
|
2015-12-16 21:47:02 -05:00
|
|
|
|
The application runs and updates automatically as we continue to build the Tour of Heroes.
|
docs(tutorial): combines all 4 sections + revisions/updates to a.53, hides 3&4
closes #488
ToH History (oldest-to-latest):
----------------------------
created code example/snippets files for use with +makeExample, replace usage of "pre.prettyprint.lang-bash" with this: code-example(format="." language="bash").
fixed spelling errors in examples file path used by +makeExample
changed usage of "code-example" to "+makeExample"
adding code example/snippets files used in toh 1
fixed example file paths, replaced "pre.prettyprint.lang-bash" with "code-example. "
(docs) toh-pt3 initial state
created code examples for display in jade, starting conversion of Google doc and trying +makeExample rendering
all text copied from doc to jade, still some styling and formatting to perform
completed conversion and styling, moved toh3 example files to "tutorial" folder under _examples
created specific code example files for chapter toh 3 and re-pathed references in +makeExample
minor edit
docs) toh combined - initial combined commit
updated ToH for a.52
tons of changes, including de-kebab-ing, removed src folder, updated tsconfig too
fixing snippets using incorrect ending input tag
using inline html and css for the app.component.
ToH Part 1 Code: updated the imports, removed obsolete directive delcarations
ToH Code Part 1: updated to use imports, interface. will hit others soon
toh part 1: ngModel fix
toh part1: removed obsolete story that referred to how we used to have to import and declare all directives we used. yay!
ToH Part 1: updated to use `boot.ts` and `app.component.ts`. this affected the partials, snippets, and the story.
toh part 1: using `npm run go`
toh parts 1 -4: modified all places to use `npm run go`
toh part 1: refactor for jade
toh part 1: fixing the code samples
toh part 2: seeping through the story
toh part 2: fixing snippets.
toh part 2: replaced ngClass with class.selected
toh part 2: removed whitespace
toh part 2: added final state to the code
toh: fixing paths
toh part 4: fixing src/app path to app
toh part 3: fixing folder path
toh part 2: fixed typo
toh part 2: typo on ngModel
toh part 2: added ngif
toh part 2: removed old hero property. moved the details lower, where we need it
toh index: updated hero list image to show consistent styling as the other images here
QS spelling error (targes -> targets)
tweeks: space and ngIF
2015-11-15 21:04:43 -05:00
|
|
|
|
|
2015-12-14 23:05:13 -05:00
|
|
|
|
## Creating a Hero Service
|
2015-12-16 21:47:02 -05:00
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
### Create the HeroService
|
|
|
|
|
Create a file in the `app` folder called `hero.service.ts`.
|
2015-12-14 23:05:13 -05:00
|
|
|
|
.l-sub-section
|
docs(tutorial): combines all 4 sections + revisions/updates to a.53, hides 3&4
closes #488
ToH History (oldest-to-latest):
----------------------------
created code example/snippets files for use with +makeExample, replace usage of "pre.prettyprint.lang-bash" with this: code-example(format="." language="bash").
fixed spelling errors in examples file path used by +makeExample
changed usage of "code-example" to "+makeExample"
adding code example/snippets files used in toh 1
fixed example file paths, replaced "pre.prettyprint.lang-bash" with "code-example. "
(docs) toh-pt3 initial state
created code examples for display in jade, starting conversion of Google doc and trying +makeExample rendering
all text copied from doc to jade, still some styling and formatting to perform
completed conversion and styling, moved toh3 example files to "tutorial" folder under _examples
created specific code example files for chapter toh 3 and re-pathed references in +makeExample
minor edit
docs) toh combined - initial combined commit
updated ToH for a.52
tons of changes, including de-kebab-ing, removed src folder, updated tsconfig too
fixing snippets using incorrect ending input tag
using inline html and css for the app.component.
ToH Part 1 Code: updated the imports, removed obsolete directive delcarations
ToH Code Part 1: updated to use imports, interface. will hit others soon
toh part 1: ngModel fix
toh part1: removed obsolete story that referred to how we used to have to import and declare all directives we used. yay!
ToH Part 1: updated to use `boot.ts` and `app.component.ts`. this affected the partials, snippets, and the story.
toh part 1: using `npm run go`
toh parts 1 -4: modified all places to use `npm run go`
toh part 1: refactor for jade
toh part 1: fixing the code samples
toh part 2: seeping through the story
toh part 2: fixing snippets.
toh part 2: replaced ngClass with class.selected
toh part 2: removed whitespace
toh part 2: added final state to the code
toh: fixing paths
toh part 4: fixing src/app path to app
toh part 3: fixing folder path
toh part 2: fixed typo
toh part 2: typo on ngModel
toh part 2: added ngif
toh part 2: removed old hero property. moved the details lower, where we need it
toh index: updated hero list image to show consistent styling as the other images here
QS spelling error (targes -> targets)
tweeks: space and ngIF
2015-11-15 21:04:43 -05:00
|
|
|
|
:marked
|
2015-12-16 21:47:02 -05:00
|
|
|
|
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 with lower dash case (AKA kebab-case).
|
|
|
|
|
The `SpecialSuperHeroService` would be defined in the `special-super-hero.service.ts` file.
|
|
|
|
|
:marked
|
|
|
|
|
We name the class `HeroService` and export it for others to import.
|
|
|
|
|
|
|
|
|
|
+makeExample('toh-4/ts/app/hero.service.1.ts', 'empty-class', 'hero.service.ts (exported class)')(format=".")
|
2015-12-14 23:05:13 -05:00
|
|
|
|
|
|
|
|
|
:marked
|
2015-12-16 21:47:02 -05:00
|
|
|
|
### Injectable Services
|
|
|
|
|
Notice that we imported the Angular `Injectable` function and applied that function as an `@Injectable()` decorator.
|
|
|
|
|
.callout.is-helpful
|
|
|
|
|
:marked
|
|
|
|
|
**Don't forget the parentheses!** Neglecting them leads to an error that's difficult to diagnose.
|
|
|
|
|
:marked
|
|
|
|
|
TypeScript sees the `@Injectable()` decorator and emits metadata about our service,
|
|
|
|
|
metadata that Angular may need to inject other dependencies into this service.
|
2015-12-14 23:05:13 -05:00
|
|
|
|
|
2015-12-16 21:47:02 -05:00
|
|
|
|
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.
|
2015-12-14 23:05:13 -05:00
|
|
|
|
|
2015-12-16 21:47:02 -05:00
|
|
|
|
:marked
|
|
|
|
|
### Getting Heroes
|
|
|
|
|
Add a `getHeroes` method stub.
|
|
|
|
|
+makeExample('toh-4/ts/app/hero.service.1.ts', 'getHeroes-stub', 'hero.service.ts ( getHeroes stub)')(format=".")
|
|
|
|
|
:marked
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 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.
|
|
|
|
|
|
2015-12-23 12:42:57 -05:00
|
|
|
|
Cut the `HEROES` array from `app.component.ts` and paste it to a new file in the `app` folder named `mock-heroes.ts`.
|
2015-12-16 21:47:02 -05:00
|
|
|
|
We copy the `import {Hero} ...` statement as well because the heroes array uses the `Hero` interface.
|
|
|
|
|
|
|
|
|
|
+makeExample('toh-4/ts/app/mock-heroes.ts', null, 'mock-heroes.ts (Heroes array)')
|
|
|
|
|
:marked
|
|
|
|
|
We export the `HEROES` constant so we can import it elsewhere — such as our `HeroService`.
|
2015-12-14 23:05:13 -05:00
|
|
|
|
|
2015-12-16 21:47:02 -05:00
|
|
|
|
Meanwhile, back in `app.component.ts` where we cut away the `HEROES` array,
|
|
|
|
|
we leave behind an uninitialized `heroes` property:
|
|
|
|
|
+makeExample('toh-4/ts/app/app.component.1.ts', 'heroes-prop', 'app.component.ts (heroes property)')(format=".")
|
|
|
|
|
:marked
|
|
|
|
|
### Return Mocked Heroes
|
|
|
|
|
Back in the `HeroService` we import the mock `HEROES` and return it from the `getHeroes` method.
|
|
|
|
|
Our `HeroService` looks like this:
|
|
|
|
|
+makeExample('toh-4/ts/app/hero.service.1.ts', null, 'hero.service.ts')(format=".")
|
|
|
|
|
:marked
|
|
|
|
|
### Use the Hero Service
|
|
|
|
|
We're ready to use the `HeroService` in other components starting with our `AppComponent`.
|
2015-12-14 23:05:13 -05:00
|
|
|
|
|
2015-12-16 21:47:02 -05:00
|
|
|
|
We begin, as usual, by importing the thing we want to use, the `HeroService`.
|
|
|
|
|
+makeExample('toh-4/ts/app/app.component.ts', 'hero-service-import', 'app.component.ts (import HeroService)')
|
|
|
|
|
:marked
|
|
|
|
|
Importing the service allows us to *reference* it in our code.
|
|
|
|
|
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:
|
|
|
|
|
+makeExample('toh-4/ts/app/app.component.1.ts', 'new-service')(format=".")
|
|
|
|
|
:marked
|
|
|
|
|
That's a bad idea for several reasons including
|
|
|
|
|
|
|
|
|
|
* 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.
|
|
|
|
|
|
|
|
|
|
* 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.
|
|
|
|
|
|
2015-12-22 13:25:30 -05:00
|
|
|
|
*What if ... what if ... Hey, we've got work to do!*
|
2015-12-16 21:47:02 -05:00
|
|
|
|
|
|
|
|
|
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 of *new*:
|
|
|
|
|
1. we add a constructor.
|
|
|
|
|
1. we add to the component's `providers` metadata
|
|
|
|
|
|
|
|
|
|
Here's the constructor:
|
|
|
|
|
+makeExample('toh-4/ts/app/app.component.1.ts', 'ctor', 'app.component.ts (constructor)')
|
|
|
|
|
:marked
|
|
|
|
|
The constructor itself does nothing. The parameter simultaneously
|
|
|
|
|
defines a private `_heroService` property and identifies it as a `HeroService` injection site.
|
2015-12-14 23:05:13 -05:00
|
|
|
|
.l-sub-section
|
docs(tutorial): combines all 4 sections + revisions/updates to a.53, hides 3&4
closes #488
ToH History (oldest-to-latest):
----------------------------
created code example/snippets files for use with +makeExample, replace usage of "pre.prettyprint.lang-bash" with this: code-example(format="." language="bash").
fixed spelling errors in examples file path used by +makeExample
changed usage of "code-example" to "+makeExample"
adding code example/snippets files used in toh 1
fixed example file paths, replaced "pre.prettyprint.lang-bash" with "code-example. "
(docs) toh-pt3 initial state
created code examples for display in jade, starting conversion of Google doc and trying +makeExample rendering
all text copied from doc to jade, still some styling and formatting to perform
completed conversion and styling, moved toh3 example files to "tutorial" folder under _examples
created specific code example files for chapter toh 3 and re-pathed references in +makeExample
minor edit
docs) toh combined - initial combined commit
updated ToH for a.52
tons of changes, including de-kebab-ing, removed src folder, updated tsconfig too
fixing snippets using incorrect ending input tag
using inline html and css for the app.component.
ToH Part 1 Code: updated the imports, removed obsolete directive delcarations
ToH Code Part 1: updated to use imports, interface. will hit others soon
toh part 1: ngModel fix
toh part1: removed obsolete story that referred to how we used to have to import and declare all directives we used. yay!
ToH Part 1: updated to use `boot.ts` and `app.component.ts`. this affected the partials, snippets, and the story.
toh part 1: using `npm run go`
toh parts 1 -4: modified all places to use `npm run go`
toh part 1: refactor for jade
toh part 1: fixing the code samples
toh part 2: seeping through the story
toh part 2: fixing snippets.
toh part 2: replaced ngClass with class.selected
toh part 2: removed whitespace
toh part 2: added final state to the code
toh: fixing paths
toh part 4: fixing src/app path to app
toh part 3: fixing folder path
toh part 2: fixed typo
toh part 2: typo on ngModel
toh part 2: added ngif
toh part 2: removed old hero property. moved the details lower, where we need it
toh index: updated hero list image to show consistent styling as the other images here
QS spelling error (targes -> targets)
tweeks: space and ngIF
2015-11-15 21:04:43 -05:00
|
|
|
|
:marked
|
2015-12-16 21:47:02 -05:00
|
|
|
|
We prefix private variables with an underscore (_) to warn readers of our code
|
|
|
|
|
that this variable is not part of the component's public API.
|
2015-12-14 23:05:13 -05:00
|
|
|
|
:marked
|
2015-12-16 21:47:02 -05:00
|
|
|
|
Now Angular will know to supply an instance of the `HeroService` when it creates a new `AppComponent`.
|
|
|
|
|
|
|
|
|
|
Angular has to get that instance from somewhere. That's the role of the Angular *Dependency Injector*.
|
|
|
|
|
The **Injector** has a **container** of previously created services.
|
|
|
|
|
Either it finds and returns a pre-existing `HeroService` from its container or it creates a new instance, adds
|
|
|
|
|
it to the container, and returns it to Angular.
|
2015-12-14 23:05:13 -05:00
|
|
|
|
|
2015-12-16 21:47:02 -05:00
|
|
|
|
.l-sub-section
|
|
|
|
|
:marked
|
|
|
|
|
Learn more about Dependency Injection in the [Dependency Injection](../guide/dependency-injection.html) chapter.
|
|
|
|
|
:marked
|
|
|
|
|
The *injector* does not know yet how to create a `HeroService`.
|
|
|
|
|
If we ran our code now, Angular would fail with an error:
|
2015-12-14 23:05:13 -05:00
|
|
|
|
code-example(format="." language="html").
|
|
|
|
|
EXCEPTION: No provider for HeroService! (AppComponent -> HeroService)
|
2015-12-16 21:47:02 -05:00
|
|
|
|
:marked
|
|
|
|
|
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
|
|
|
|
|
in the `@Component` call.
|
2015-12-14 23:05:13 -05:00
|
|
|
|
|
2015-12-16 21:47:02 -05:00
|
|
|
|
+makeExample('toh-4/ts/app/app.component.1.ts', 'providers', 'app.component.ts (providing HeroService)')
|
|
|
|
|
:marked
|
|
|
|
|
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.
|
|
|
|
|
<a id="child-component"></a>
|
|
|
|
|
.l-sub-section
|
|
|
|
|
:marked
|
|
|
|
|
### Services and the component tree
|
|
|
|
|
|
|
|
|
|
Recall that the `AppComponent` creates an instance of `HeroDetail` by virtue of the
|
|
|
|
|
`<my-hero-detail>` tag at the bottom of its template. That `HeroDetail` is a child of the `AppComponent`.
|
|
|
|
|
|
|
|
|
|
If the `HeroDetailComponent` needed its parent component's `HeroService`,
|
|
|
|
|
it would ask Angular to inject the service into its constructor which would look just like the one for `AppComponent`:
|
2016-01-04 22:03:23 -05:00
|
|
|
|
+makeExample('toh-4/ts/app/app.component.1.ts', 'ctor', 'hero-detail.component.ts (constructor)')
|
2015-12-16 21:47:02 -05:00
|
|
|
|
:marked
|
2016-02-01 13:52:20 -05:00
|
|
|
|
The `HeroDetailComponent` must *not* repeat its parent's `providers` array! Guess [why](#shadow-provider).
|
2015-12-16 21:47:02 -05:00
|
|
|
|
|
|
|
|
|
The `AppComponent` is the top level component of our application.
|
|
|
|
|
There should be only one instance of that component and only one instance of the `HeroService` in our entire app.
|
2015-12-14 23:05:13 -05:00
|
|
|
|
:marked
|
2015-12-16 21:47:02 -05:00
|
|
|
|
### *getHeroes* in the *AppComponent*
|
|
|
|
|
We've got the service in a `_heroService` private variable. Let's use it.
|
|
|
|
|
|
|
|
|
|
We pause to think. We can call the service and get the data in one line.
|
|
|
|
|
+makeExample('toh-4/ts/app/app.component.1.ts', 'get-heroes')(format=".")
|
|
|
|
|
:marked
|
|
|
|
|
We don't really need a dedicated method to wrap one line. We write it anyway:
|
|
|
|
|
+makeExample('toh-4/ts/app/app.component.1.ts', 'getHeroes', 'app.component.ts (getHeroes)')(format=".")
|
|
|
|
|
:marked
|
|
|
|
|
### 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*!
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
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 — like calling a server! — before we tell it to do so.
|
|
|
|
|
|
|
|
|
|
If not the constructor, something has to call `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:
|
|
|
|
|
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.
|
|
|
|
|
.l-sub-section
|
|
|
|
|
:marked
|
|
|
|
|
Learn more about lifecycle hooks in the [Lifecycle Hooks](../guide/lifecycle-hooks.html) chapter.
|
|
|
|
|
:marked
|
|
|
|
|
Here's the essential outline for the `OnInit` interface:
|
|
|
|
|
+makeExample('toh-4/ts/app/app.component.1.ts', 'on-init', 'app.component.ts (OnInit protocol)')(format=".")
|
|
|
|
|
:marked
|
|
|
|
|
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`.
|
|
|
|
|
+makeExample('toh-4/ts/app/app.component.1.ts', 'ng-on-init', 'app.component.ts (OnInit protocol)')(format=".")
|
|
|
|
|
:marked
|
|
|
|
|
Our application should be running as expected, showing a list of heroes and a hero detail view
|
|
|
|
|
when we click on a hero name.
|
|
|
|
|
|
|
|
|
|
We're getting closer. But something isn't quite right.
|
|
|
|
|
|
|
|
|
|
## Async Services and Promises
|
|
|
|
|
Our `HeroService` returns a list of mock heroes immediately.
|
|
|
|
|
Its `getHeroes` signature is synchronous
|
|
|
|
|
+makeExample('toh-4/ts/app/app.component.1.ts', 'get-heroes')(format=".")
|
|
|
|
|
:marked
|
|
|
|
|
Ask for heroes and they are there in the returned result.
|
|
|
|
|
|
|
|
|
|
Someday we're going to get heroes from a remote server. We don’t call http yet, but we aspire to in later chapters.
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
We'll have to use some kind of asynchronous technique and that will change the signature of our `getHeroes` method.
|
|
|
|
|
|
|
|
|
|
We'll use *promises*.
|
|
|
|
|
|
|
|
|
|
### The Hero Service makes a promise
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
.l-sub-section
|
|
|
|
|
:marked
|
|
|
|
|
We are simplifying. Learn about ES2015 Promises [here](http://www.datchley.name/es6-promises/) and elsewhere on the web.
|
|
|
|
|
:marked
|
|
|
|
|
Update the `HeroService` with this promise-returning `getHeroes` method:
|
|
|
|
|
+makeExample('toh-4/ts/app/hero.service.ts', 'get-heroes', 'hero.service.ts (getHeroes)')(format=".")
|
|
|
|
|
:marked
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
### Act on the Promise
|
|
|
|
|
Returning to the `AppComponent` and its `getHeroes` method, we see that it still looks like this:
|
|
|
|
|
+makeExample('toh-4/ts/app/app.component.1.ts', 'getHeroes', 'app.component.ts (getHeroes - old)')(format=".")
|
|
|
|
|
:marked
|
|
|
|
|
As a result of our change to `HeroService`, we're now setting `this.heroes` to a promise rather than an array of heroes.
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
We pass our callback function as an argument to the promise's **then** method:
|
|
|
|
|
+makeExample('toh-4/ts/app/app.component.ts', 'get-heroes', 'app.component.ts (getHeroes - revised)')(format=".")
|
2015-12-22 13:25:30 -05:00
|
|
|
|
.l-sub-section
|
|
|
|
|
:marked
|
|
|
|
|
The [ES2015 arrow function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions)
|
2016-01-16 17:00:45 -05:00
|
|
|
|
in the callback is more succinct than the equivalent function expression and gracefully handles *this*.
|
2015-12-16 21:47:02 -05:00
|
|
|
|
:marked
|
|
|
|
|
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
|
|
|
|
|
responding to a name selection with a detail view.
|
|
|
|
|
.l-sub-section
|
|
|
|
|
:marked
|
|
|
|
|
Checkout the "[Take it slow](#slow)" appendix to see what the app might be like with a poor connection.
|
|
|
|
|
:marked
|
|
|
|
|
### Review the App Structure
|
2015-12-14 23:05:13 -05:00
|
|
|
|
Let’s verify that we have the following structure after all of our good refactoring in this chapter:
|
|
|
|
|
|
|
|
|
|
.filetree
|
|
|
|
|
.file angular2-tour-of-heroes
|
|
|
|
|
.children
|
|
|
|
|
.file node_modules
|
|
|
|
|
.file app
|
|
|
|
|
.children
|
|
|
|
|
.file app.component.ts
|
|
|
|
|
.file hero.ts
|
|
|
|
|
.file hero-detail.component.ts
|
|
|
|
|
.file hero.service.ts
|
2016-01-28 19:15:26 -05:00
|
|
|
|
.file main.ts
|
2015-12-14 23:05:13 -05:00
|
|
|
|
.file mock-heroes.ts
|
|
|
|
|
.file index.html
|
|
|
|
|
.file package.json
|
|
|
|
|
.file tsconfig.json
|
|
|
|
|
:marked
|
2015-12-16 21:47:02 -05:00
|
|
|
|
Here are the code files we discussed in this chapter.
|
|
|
|
|
|
|
|
|
|
+makeTabs(`
|
|
|
|
|
toh-4/ts/app/hero.service.ts,
|
|
|
|
|
toh-4/ts/app/app.component.ts,
|
|
|
|
|
toh-4/ts/app/mock-heroes.ts
|
|
|
|
|
`,'',`
|
|
|
|
|
app/hero.service.ts,
|
|
|
|
|
app/app.component.ts,
|
|
|
|
|
app/mock-heroes.ts
|
|
|
|
|
`)
|
|
|
|
|
:marked
|
|
|
|
|
## The Road We’ve Travelled
|
|
|
|
|
Let’s take stock of what we’ve built.
|
2015-12-14 23:05:13 -05:00
|
|
|
|
|
2015-12-16 21:47:02 -05:00
|
|
|
|
* 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 for part 4](/resources/live-examples/toh-4/ts/plnkr.html)
|
2015-12-14 23:05:13 -05:00
|
|
|
|
|
2015-12-16 21:47:02 -05:00
|
|
|
|
### 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, we’ll learn how to design it to make it easier to grow and maintain.
|
|
|
|
|
We’ll learn more about these tasks in the coming chapters.
|
|
|
|
|
|
|
|
|
|
.l-main-section
|
|
|
|
|
<a id="slow"></a>
|
|
|
|
|
:marked
|
|
|
|
|
### Appendix: Take it slow
|
|
|
|
|
|
|
|
|
|
We can simulate a slow connection.
|
|
|
|
|
|
2016-02-01 13:52:20 -05:00
|
|
|
|
Import the `Hero` symbol and add the following `getHeroesSlowly` method to the `HeroService`
|
2015-12-16 21:47:02 -05:00
|
|
|
|
+makeExample('toh-4/ts/app/hero.service.ts', 'get-heroes-slowly', 'hero.service.ts (getHeroesSlowy)')(format=".")
|
|
|
|
|
:marked
|
|
|
|
|
Like `getHeroes`, it also returns a promise.
|
|
|
|
|
But this promise waits 2 seconds before resolving the promise with mock heroes.
|
|
|
|
|
|
|
|
|
|
Back in the `AppComponent`, swap `_heroService.getHeroesSlowly` for `_heroService.getHeroes`
|
|
|
|
|
and see how the app behaves.
|
|
|
|
|
|
|
|
|
|
.l-main-section
|
|
|
|
|
<a id="shadow-provider"></a>
|
|
|
|
|
:marked
|
|
|
|
|
### Appendix: Shadowing the parent's service
|
|
|
|
|
|
|
|
|
|
We stated [earlier](#child-component) that if we injected the parent `AppComponent` `HeroService`
|
2015-12-22 13:25:30 -05:00
|
|
|
|
into the `HeroDetailComponent`, *we must not add a providers array* to the `HeroDetailComponent` metadata.
|
|
|
|
|
|
2015-12-16 21:47:02 -05:00
|
|
|
|
Why? Because that tells Angular to create a new instance of the `HeroService` at the `HeroDetailComponent` level.
|
|
|
|
|
The `HeroDetailComponent` doesn't want its *own* service instance; it wants its *parent's* service instance.
|
|
|
|
|
Adding the `providers` array creates a new service instance that shadows the parent instance.
|
|
|
|
|
|
|
|
|
|
Think carefully about where and when to register a provider.
|
2016-01-04 22:03:23 -05:00
|
|
|
|
Understand the scope of that registration. Be careful not to create a new service instance at the wrong level.
|