diff --git a/public/docs/_examples/server-communication/ts/app/toh/hero.service.1.ts b/public/docs/_examples/server-communication/ts/app/toh/hero.service.1.ts
index 97f5e01d35..b785ee9497 100644
--- a/public/docs/_examples/server-communication/ts/app/toh/hero.service.1.ts
+++ b/public/docs/_examples/server-communication/ts/app/toh/hero.service.1.ts
@@ -11,7 +11,8 @@ import {Hero} from './hero';
export class HeroService {
constructor (private http: Http) {}
- private _heroesUrl = 'app/heroes';
+ // URL to web api
+ private _heroesUrl = 'app/heroes.json';
// #docregion methods
getHeroes () {
diff --git a/public/docs/_examples/server-communication/ts/app/toh/hero.service.ts b/public/docs/_examples/server-communication/ts/app/toh/hero.service.ts
index 216e0acd4a..c9a06c233d 100644
--- a/public/docs/_examples/server-communication/ts/app/toh/hero.service.ts
+++ b/public/docs/_examples/server-communication/ts/app/toh/hero.service.ts
@@ -15,9 +15,19 @@ import {Observable} from 'rxjs/Observable';
@Injectable()
export class HeroService {
constructor (private http: Http) {}
+// #enddocregion
+// #enddocregion v1
+
+ /*
+ // #docregion endpoint-json
+ private _heroesUrl = 'app/heroes.json'; // URL to JSON file
+ // #enddocregion endpoint-json
+ */
+// #docregion
+// #docregion v1
// #docregion endpoint
- private _heroesUrl = 'app/heroes';
+ private _heroesUrl = 'app/heroes'; // URL to web api
// #enddocregion endpoint
// #docregion methods
diff --git a/public/docs/ts/latest/guide/server-communication.jade b/public/docs/ts/latest/guide/server-communication.jade
index 4583bcf317..3e618d0b9a 100644
--- a/public/docs/ts/latest/guide/server-communication.jade
+++ b/public/docs/ts/latest/guide/server-communication.jade
@@ -84,11 +84,11 @@ figure.image-display
Below the button is an optional error message.
- ### The `HeroListComponent` class
+ ### The *HeroListComponent* class
We [inject](dependency-injection.html) the `HeroService` into the constructor.
That's the instance of the `HeroService` that we provided in the parent shell `TohComponent`.
- Notice that the component **does not talk to the server directly!**.
+ Notice that the component **does not talk to the server directly!**
The component doesn't know or care how we get the data.
Those details it delegates to the `heroService` class (which we'll get to in a moment).
This is a golden rule: *always delegate data access to a supporting service class*.
@@ -132,9 +132,15 @@ figure.image-display
Look closely at how we call `http.get`
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'http-get-v1', 'app/toh/hero.service.ts (http.get)')(format=".")
:marked
- We pass the resource URL to `get` and it calls the server which returns
- data from the `heroes.json` file.
-
+ We pass the resource URL to `get` and it calls the server which should return heroes.
+.l-sub-section
+ :marked
+ It *will* return heroes once we've set up the [in-memory web api](in-mem-web-api)
+ described in the appendix below.
+
+ Alternatively, we can (temporarily) target a JSON file by changing the endpoint URL:
+ +makeExample('server-communication/ts/app/toh/hero.service.ts', 'endpoint-json')(format=".")
+:marked
The return value may surprise us. Many of us would expect a
[promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
@@ -199,13 +205,17 @@ figure.image-display
The Angular HTTP client follows the ES2015 specification for the
[response object](https://fetch.spec.whatwg.org/#response-class) returned by the `Fetch` function.
That spec defines a `json()` method that parses the response body into a JavaScript object.
-
- We must also know the shape of the data returned by the server API.
+.l-sub-section
+ :marked
We shouldn't expect `json()` to return the heroes array directly.
The server we're calling always wraps JSON results in an object with a `data`
property. We have to unwrap it to get the heroes.
This is conventional web api behavior, driven by
[security concerns](https://www.owasp.org/index.php/OWASP_AJAX_Security_Guidelines#Always_return_JSON_with_an_Object_on_the_outside).
+.alert.is-important
+ :marked
+ Make no assumptions about the server API.
+ Not all servers return an object with a `data` property.
:marked
### Do not return the response object
Our `getHeroes()` could have returned the `Observable`.
@@ -230,7 +240,7 @@ figure.image-display
In this simple app we provide rudimentary error handling in both the service and the component.
We use the Observable `catch` operator on the service level.
- It takes an error handling function with the failed `Response` object as the argument.
+ It takes an error handling function with the failed `Response` object as the argument.
Our service handler, `errorHandler`, logs the response to the console,
transforms the error into a user-friendly message, and returns the message in a new, failed observable via `Observable.throw`.
@@ -249,9 +259,7 @@ figure.image-display
.l-sub-section
:marked
- Try messing with the value of the api endpoint in the `HeroService` and watch it fail
- +makeExample('server-communication/ts/app/toh/hero.service.ts', 'endpoint', 'app/toh/hero.service.ts (endpoint)')
-
+ Want to see it fail? Reset the api endpoint in the `HeroService` to a bad value. Remember to restore it!
:marked
@@ -317,10 +325,10 @@ code-example(format="." language="javascript").
:marked
### JSON results
As with `get`, we extract the data from the response with `json()` and unwrap the hero via the `data` property.
-.l-sub-section
+.alert.is-important
:marked
- *Reminder*: we must know the shape of the data returned by the server.
- The sample web api returns the new hero wrapped in an object with a `data` property.
+ Know the shape of the data returned by the server.
+ *This* web api returns the new hero wrapped in an object with a `data` property.
A different api might just return the hero in which case we'd omit the `data` de-reference.
:marked
Back in the `HeroListComponent`, we see that *its* `addHero` method subscribes to the observable returned by the *service's* `addHero` method.
@@ -337,7 +345,7 @@ code-example(format="." language="javascript").
.l-sub-section
:marked
While promises may be more familiar, observables have many advantages.
- Don't rush to promises until you've observables a chance.
+ Don't rush to promises until you give observables a chance.
:marked
Let's rewrite the `HeroService` using promises , highlighting just the parts that are different.
+makeTabs(
@@ -521,7 +529,7 @@ figure.image-display
+makeExample('server-communication/ts/app/wiki/wiki-smart.component.ts', 'import-subject')
:marked
Each search term is a string, so we create a new `Subject` of type `string` called `_searchTermStream`.
- After every keystroke, the `search` method adds the the search box value to that stream
+ After every keystroke, the `search` method adds the search box value to that stream
via the subject's `next` method.
+makeExample('server-communication/ts/app/wiki/wiki-smart.component.ts', 'subject')(format='.')
:marked
@@ -560,36 +568,48 @@ figure.image-display
to mitigate the [security risk](http://stackoverflow.com/questions/3503102/what-are-top-level-json-arrays-and-why-are-they-a-security-risk)
posed by top-level JSON arrays.
:marked
- Our sample saves data. We can't save to a JSON file. We'll need a server ... or a server simulation.
- This sample uses an in-memory web api simulator that we first installed with `npm`:
-code-example.
+ We'd set the endpoint to the JSON file like this:
++makeExample('server-communication/ts/app/toh/hero.service.ts', 'endpoint-json')(format=".")
+:marked
+ The *get heroes* scenario would work.
+ But we want to *save* data too. We can't save changes to a JSON file. We need a web api server.
+
+ We didn't want the hassle of setting up and maintaining a real server for this chapter.
+ So we turned to an *in-memory web api simulator* instead.
+ You too can use it in your own development while waiting for a real server to arrive.
+
+ First, install it with `npm`:
+code-example(language="bash").
npm install a2-in-memory-web-api --save
:marked
- and then loaded it in our html below the angular script:
+ Then load the script in the `index.html` below angular:
+makeExample('server-communication/ts/index.html', 'in-mem-web-api', 'index.html')(format=".")
:marked
- The in-memory web api gets its data from a class with a `createDb()` method that returns
+ The *in-memory web api* gets its data from a class with a `createDb()` method that returns
a "database" object whose keys are collection names ("heroes")
and whose values are arrays of objects in those collections.
Here's the class we created for this sample by copy-and-pasting the JSON data:
+makeExample('server-communication/ts/app/hero-data.ts', null, 'app/hero-data.ts')(format=".")
:marked
- Finally, we tell Angular to direct its http requests to the in-memory web api service rather
- than to a remote server.
+ We update the `HeroService` endpoint to the location of the web api data.
++makeExample('server-communication/ts/app/toh/hero.service.ts', 'endpoint')(format=".")
+:marked
+ Finally, we tell Angular itself to direct its http requests to the *in-memory web api* rather
+ than externally to a remote server.
- This redirection is easy in Angular because `http` delegates the client/server communication tasks
+ This redirection is easy because Angular's `http` delegates the client/server communication tasks
to a helper service called the `XHRBackend`.
- To enable our server simulation, we replace the `XHRBackend` service with
- the in-memory web api *backend* using standard Angular provider registration
- in the `TohComponent`. We supply the hero data to the in-memory web api in the same way at the same time.
+ To enable our server simulation, we replace the default `XHRBackend` service with
+ the *in-memory web api service* using standard Angular provider registration
+ in the `TohComponent`. We initialize the *in-memory web api* with mock hero data at the same time.
Here are the pertinent details, excerpted from `TohComponent`, starting with the imports:
+makeExample('server-communication/ts/app/toh/toh.component.ts', 'in-mem-web-api-imports', 'toh.component.ts (web api imports)')(format=".")
:marked
- Then add these two provider definitions to the component's `providers` array in metadata:
-+makeExample('server-communication/ts/app/toh/toh.component.ts', 'in-mem-web-api-providers', 'toh.component.ts (web api providers')(format=".")
+ Then we add the following two provider definitions to the `providers` array in component metadata:
++makeExample('server-communication/ts/app/toh/toh.component.ts', 'in-mem-web-api-providers', 'toh.component.ts (web api providers)')(format=".")
:marked
See the full source code in the [live example](/resources/live-examples/server-communication/ts/plnkr.html).