* docs(toh-6/ts): minor edits and enhancements
Changes to prose:
- Complete TODO item of displaying `heroes.component` errors.
- Mainly copyedits.
- Add of blocks statements so that prose can be used on Dart side.
- Show excerpt and briefly explain of changes (previously missing):
- `app/hero-detail.component.html`
- `app/heroes.component.ts` error handling
- Add missing file to changed/added files listing and makeTabs
- `toh-6/ts/app/in-memory-data.service.ts,
- `toh-6/ts/sample.css`
Code changes:
- Mainly copyedits
- Renamed `heroes.component.ts` `delete` to `deleteHero` to match
naming of other methods
* remove unnecessary change relative to toh-5
- var _Http = 'Http'; // Angular `Http` library name.
- var _Angular_Http = 'Angular <code>Http</code>'
- var _Angular_http_library = 'Angular HTTP library'
- var _HTTP_PROVIDERS = 'HTTP_PROVIDERS'
- var _JSON_stringify = 'JSON.stringify'
:marked
# Getting and Saving Data with HTTP
@ -7,7 +15,7 @@ include ../_util-fns
Now they want to get the hero data from a server, let users add, edit, and delete heroes,
and save these changes back to the server.
In this chapter we teach our application to make the corresponding http calls to a remote server's web api.
In this chapter we teach our application to make the corresponding HTTP calls to a remote server's web API.
p Run the #[+liveExampleLink2('', 'toh-6')] for this part.
@ -17,43 +25,50 @@ p Run the #[+liveExampleLink2('', 'toh-6')] for this part.
In the [previous chapter](toh-pt5.html), we learned to navigate between the dashboard and the fixed heroes list, editing a selected hero along the way.
That's our starting point for this chapter.
### Keep the app transpiling and running
Open a terminal/console window and enter the following command to
start the TypeScript compiler, start the server, and watch for changes:
block start-server-and-watch
:marked
### Keep the app transpiling and running
Open a terminal/console window and enter the following command to
start the TypeScript compiler, start the server, and watch for changes:
code-example(language="bash").
npm start
code-example(language="bash").
npm start
:marked
The application runs and updates automatically as we continue to build the Tour of Heroes.
.l-main-section
:marked
## Prepare for Http
.l-main-section#http-providers
h1 Providing HTTP Services
block http-library
:marked
`Http` is ***not*** a core Angular module.
It's Angular's optional approach to web access and it exists as a separate add-on module called `@angular/http`,
shipped in a separate script file as part of the Angular npm package.
`Http` is ***not*** a core Angular module.
It's Angular's optional approach to web access and it exists as a separate add-on module called `@angular/http`,
shipped in a separate script file as part of the Angular npm package.
Fortunately we're ready to import from `@angular/http` because `systemjs.config` configured *SystemJS* to load that library when we need it.
Fortunately we're ready to import from `@angular/http` because `systemjs.config` configured *SystemJS* to load that library when we need it.
:marked
### Register (provide) *http* services
Our app will depend upon the Angular `http` service which itself depends upon other supporting services.
The `HTTP_PROVIDERS` array from `@angular/http` library holds providers for the complete set of http services.
### Register (provide) *HTTP* services
We should be able to access these services from anywhere in the application.
So we register them in the `bootstrap` method of `main.ts` where we
block http-providers
:marked
Our app will depend upon the Angular `http` service which itself depends upon other supporting services.
The `HTTP_PROVIDERS` array from `@angular/http` library holds providers for the complete set of http services.
:marked
We should be able to access `!{_Http}` services from anywhere in the application.
So we register them in the `bootstrap` call of `main.!{_docsFor}` where we
launch the application and its root `AppComponent`.
In this demo service we log the error to the console; we should do better in real life.
We've also decided to return a user friendly form of the error to
the caller in a rejected promise so that the caller can display a proper error message to the user.
the caller in a !{rejected_promise} so that the caller can display a proper error message to the user.
### Promises are Promises
### !{_Promise}s are !{_Promise}s
Although we made significant *internal* changes to `getHeroes()`, the public signature did not change.
We still return a promise. We won't have to update any of the components that call `getHeroes()`.
We still return a !{_Promise}. We won't have to update any of the components that call `getHeroes()`.
.l-main-section
:marked
## Add, Edit, Delete
Our stakeholders are incredibly pleased with the added flexibility from the api integration, but it doesn't stop there. Next we want to add the capability to add, edit and delete heroes.
Our stakeholders are incredibly pleased with the added flexibility from the API integration, but it doesn't stop there. Next we want to add the capability to add, edit and delete heroes.
We'll complete `HeroService` by creating `post`, `put` and `delete` http calls to meet our new requirements.
We'll complete `HeroService` by creating `post`, `put` and `delete` methods to meet our new requirements.
:marked
### Post
We are using `post` to add new heroes. Post requests require a little bit more setup than Get requests, but the format is as follows:
We will be using `post` to add new heroes. Post requests require a little bit more setup than Get requests:
Now we create a header and set the content type to `application/json`. We'll call `JSON.stringify` before we post to convert the hero object to a string.
For Post requests we create a header and set the content type to `application/json`. We'll call `!{_JSON_stringify}` before we post to convert the hero object to a string.
### Put
`put` is used to edit a specific hero, but the structure is very similar to a `post` request. The only difference is that we have to change the url slightly by appending the id of the hero we want to edit.
Put will be used to update an individual hero. Its structure is very similar to Post requests. The only difference is that we have to change the url slightly by appending the id of the hero we want to update.
We add a `catch` to handle our errors for all three cases.
We add a `catch` to handle errors for all three methods.
:marked
### Save
We combine the call to the private `post` and `put` methods in a single `save` method. This simplifies the public api and makes the integration with `HeroDetailComponent` easier. `HeroService` determines which method to call based on the state of the `hero` object. If the hero already has an id we know it's an edit. Otherwise we know it's an add.
We combine the call to the private `post` and `put` methods in a single `save` method. This simplifies the public API and makes the integration with `HeroDetailComponent` easier. `HeroService` determines which method to call based on the state of the `hero` object. If the hero already has an id we know it's an edit. Otherwise we know it's an add.
In order to differentiate between add and edit we are adding a check to see if an id is passed in the url. If the id is absent we bind `HeroDetailComponent` to an empty `Hero` object. In either case, any edits made through the UI will be bound back to the same `hero` property.
The next step is to add a save method to `HeroDetailComponent` and call the corresponding save method in `HeroesService`.
Here we call `emit` to notify that we just added or modified a hero. `HeroesComponent` is listening for this notification and will automatically refresh the list of heroes to include our recent updates.
.l-sub-section
block hero-detail-comp-extra-imports-and-vars
:marked
The `emit` "handshake" between `HeroDetailComponent` and `HeroesComponent` is an example of component to component communication. This is a topic for another day, but we have detailed information in our <a href="/docs/ts/latest/cookbook/component-communication.html#!#child-to-parent">Component Interaction Cookbook</a>
Before we can add those methods, we need to initialize some variables with their respective imports.
In order to differentiate between add and edit we are adding a check to see if an id is passed in the url. If the id is absent we bind `HeroDetailComponent` to an empty `Hero` object. In either case, any edits made through the UI will be bound back to the same `hero` property.
:marked
Here is `HeroDetailComponent` with its new save button.
Add a save method to `HeroDetailComponent` and call the corresponding save method in `HeroesService`.
Here we call `emit` to notify that we just added or modified a hero. `HeroesComponent` is listening for this notification and will automatically refresh the list of heroes to include our recent updates.
.l-sub-section
:marked
The `emit` "handshake" between `HeroDetailComponent` and `HeroesComponent` is an example of component to component communication. This is a topic for another day, but we have detailed information in our <a href="/docs/ts/latest/cookbook/component-communication.html#!#child-to-parent">Component Interaction Cookbook</a>
:marked
Here is `HeroDetailComponent` with its new save button and the corresponding HTML.
figure.image-display
img(src='/resources/images/devguide/toh/hero-details-save-button.png' alt="Hero Details With Save Button")