diff --git a/public/docs/_examples/server-communication/ts/src/app/toh/hero.service.ts b/public/docs/_examples/server-communication/ts/src/app/toh/hero.service.ts
index 804883d0e8..5af142ace3 100644
--- a/public/docs/_examples/server-communication/ts/src/app/toh/hero.service.ts
+++ b/public/docs/_examples/server-communication/ts/src/app/toh/hero.service.ts
@@ -57,7 +57,7 @@ export class HeroService {
// #docregion error-handling
private handleError (error: Response | any) {
- // In a real world app, we might use a remote logging infrastructure
+ // In a real world app, you might use a remote logging infrastructure
let errMsg: string;
if (error instanceof Response) {
const body = error.json() || '';
diff --git a/public/docs/ts/latest/guide/server-communication.jade b/public/docs/ts/latest/guide/server-communication.jade
index 5ac57b4365..76a8c02aa1 100644
--- a/public/docs/ts/latest/guide/server-communication.jade
+++ b/public/docs/ts/latest/guide/server-communication.jade
@@ -17,27 +17,48 @@ block includes
[Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API).
The !{_Angular_http_library} simplifies application programming with the **XHR** and **JSONP** APIs.
- This page covers:
-
- - [The Tour of Heroes *HTTP* client demo](#http-client).
- - [Fetch data with http.get](#fetch-data).
-
[RxJS library](#rxjs).
- [Enable RxJS operators](#enable-rxjs-operators).
- - [Process the response object](#extract-data).
- - [Always handle errors](#error-handling).
- - [Send data to the server](#update).
- [Fall back to promises](#promises).
- - [Cross-Origin Requests: Wikipedia example](#cors).
-
- - [Search parameters](#search-parameters).
- - [More fun with observables](#more-observables).
+ # Contents
+ * [Demos](#demos)
+ * [Providing HTTP Services](#http-providers)
+ * [The Tour of Heroes *HTTP* client demo](#http-client)
+ - [The `HeroListComponent` class](#HeroListComponent)
+ * [Fetch data with `http.get()`](#fetch-data)
+ - [RxJS library](#rxjs-library)
+
+ - [Enable RxJS operators](#enable-rxjs-operators)
- - [Guarding against Cross-Site Request Forgery](#xsrf).
- - [Override default request headers (and other request options)](#override-default-request-options).
- - [Appendix: Tour of Heroes _in-memory web api_](#in-mem-web-api).
+
+ * [Process the response object](#extract-data)
+ - [Parse to `JSON`](#parse-to-json)
+ - [Do not return the response object](#no-return-response-object)
+ - [Always handle errors](#error-handling)
+ - [`HeroListComponent` error handling](#hero-list-component)
+ * [Send data to the server](#update)
+ - [Headers](#headers)
+ - [JSON results](#json-results)
+
+ - [Fall back to promises](#promises)
+
+ * [Cross-Origin Requests: Wikipedia example](#cors)
+
+ - [Search Wikipedia](#search-wikipedia)
+ - [Search parameters](#search-parameters)
+ - [The WikiComponent](#wikicomponent)
+
+ * [A wasteful app](#wasteful-app)
+ - [More fun with Observables](#more-observables)
+
+ - [Create a stream of search terms](#create-stream)
+ - [Listen for search terms](#listen-for-search-terms)
+
+
+ * [Guarding against Cross-Site Request Forgery](#xsrf)
+ * [Override default request headers (and other request options)](#override-default-request-options)
+ * [Appendix: Tour of Heroes _in-memory web api_](#in-mem-web-api)
A live example illustrates these topics.
+a#demos
.l-main-section
:marked
# Demos
@@ -49,7 +70,7 @@ block demos-list
- [The Tour of Heroes *HTTP* client demo](#http-client).
- [Fall back to !{_Promise}s](#promises).
- [Cross-Origin Requests: Wikipedia example](#cors).
- - [More fun with observables](#more-observables).
+ - [More fun with Observables](#more-observables).
:marked
The root `AppComponent` orchestrates these demos:
@@ -92,7 +113,7 @@ block http-providers
.l-sub-section
:marked
The `HttpModule` is necessary for making HTTP calls.
- Though the JsonpModule isn't necessary for plain HTTP,
+ Though the `JsonpModule` isn't necessary for plain HTTP,
there is a JSONP demo later in this page.
Loading its module now saves time.
.l-main-section#http-client
@@ -160,7 +181,7 @@ block getheroes-and-addhero
a#HeroService
.l-main-section#fetch-data
:marked
- ## Fetch data with http.get
+ ## Fetch data with _http.get()_
In many of the previous samples the app faked the interaction with the server by
returning mock heroes in a service like this one:
@@ -196,20 +217,21 @@ a#HeroService
Clearly this is not a promise.
In fact, the `http.get` method returns an **Observable** of HTTP Responses (`Observable`) from the RxJS library
- and `map` is one of the RxJS *operators*.
+ and `map()` is one of the RxJS *operators*.
+ a#rxjs-library
.l-main-section
:marked
## RxJS library
RxJS
is a third party library, endorsed by Angular, that implements the
- asynchronous observable pattern.
+ asynchronous Observable pattern.
All of the Developer Guide samples have installed the RxJS npm package
- because observables are used widely in Angular applications.
+ because Observables are used widely in Angular applications.
_This_ app needs it when working with the HTTP client.
- But you must take a critical extra step to make RxJS observables usable:
- you must import the RxJS operators individually.
+ But you must take a critical extra step to make RxJS Observables usable:
+ _you must import the RxJS operators individually_.
### Enable RxJS operators
The RxJS library is large.
@@ -217,19 +239,21 @@ a#HeroService
You should include only necessary features.
Each code file should add the operators it needs by importing from an RxJS library.
- The `getHeroes` method needs the `map` and `catch` operators so it imports them like this.
+ The `getHeroes()` method needs the `map()` and `catch()` operators so it imports them like this.
+makeExample('server-communication/ts/src/app/toh/hero.service.ts', 'rxjs-imports', 'src/app/app.component.ts (import rxjs)')(format=".")
.l-main-section
a#extract-data
:marked
## Process the response object
- Remember that the `getHeroes()` method used an `!{_priv}extractData` helper method to map the `!{_priv}http.get` response object to heroes:
+ Remember that the `getHeroes()` method used an `!{_priv}extractData()` helper method to map the `!{_priv}http.get` response object to heroes:
+makeExample('server-communication/ts/src/app/toh/hero.service.ts', 'extract-data', 'src/app/toh/hero.service.ts (excerpt)')(format=".")
:marked
The `response` object doesn't hold the data in a form the app can use directly.
You must parse the response data into a JSON object.
+a#parse-to-json
+:marked
### Parse to JSON
block parse-json
:marked
@@ -256,6 +280,8 @@ block parse-json
:marked
Make no assumptions about the server API.
Not all servers return an object with a `data` property.
+
+a#no-return-response-object
:marked
### Do not return the response object
The `getHeroes()` method _could_ have returned the HTTP response but this wouldn't
@@ -268,9 +294,9 @@ block parse-json
.callout.is-important
header HTTP GET is delayed
:marked
- The `!{_priv}http.get` does **not send the request just yet.** This observable is
+ The `!{_priv}http.get` does **not send the request just yet.** This Observable is
[*cold*](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/creating.md#cold-vs-hot-observables),
- which means that the request won't go out until something *subscribes* to the observable.
+ which means that the request won't go out until something *subscribes* to the Observable.
That *something* is the [HeroListComponent](#subscribe).
a#error-handling
@@ -288,9 +314,9 @@ a#error-handling
block error-handling
:marked
- The `catch` operator passes the error object from `http` to the `handleError` method.
+ The `catch()` operator passes the error object from `http` to the `handleError()` method.
The `handleError` method transforms the error into a developer-friendly message,
- logs it to the console, and returns the message in a new, failed observable via `Observable.throw`.
+ logs it to the console, and returns the message in a new, failed Observable via `Observable.throw`.
a#subscribe
a#hero-list-component
@@ -325,7 +351,7 @@ block hlc-error-handling
:marked
To implement it, you must know the server's API for creating heroes.
- [This sample's data server](#server) follows typical REST guidelines.
+ [This sample's data server](#in-mem-web-api) follows typical REST guidelines.
It expects a [`POST`](http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5) request
at the same endpoint as `GET` heroes.
It expects the new hero data to arrive in the body of the request,
@@ -339,12 +365,13 @@ code-example(format="." language="javascript").
of the new hero including its generated id. The hero arrives tucked inside a response object
with its own `data` property.
- Now that you know how the API works, implement `addHero()`as follows:
+ Now that you know how the API works, implement `addHero()` as follows:
+ifDocsFor('ts')
+makeExample('server-communication/ts/src/app/toh/hero.service.ts', 'import-request-options', 'src/app/toh/hero.service.ts (additional imports)')(format=".")
+makeExample('server-communication/ts/src/app/toh/hero.service.ts', 'addhero', 'src/app/toh/hero.service.ts (addHero)')(format=".")
+a#headers
:marked
### Headers
@@ -356,8 +383,9 @@ code-example(format="." language="javascript").
object is a new instance of `RequestOptions`, a class that allows you to specify
certain settings when instantiating a request. In this way, [headers](../api/http/index/Headers-class.html) is one of the [RequestOptions](../api/http/index/RequestOptions-class.html).
- In the `return` statement, `options` is the *third* argument of the `post` method, as shown above.
+ In the `return` statement, `options` is the *third* argument of the `post()` method, as shown above.
+a#json-results
:marked
### JSON results
@@ -366,7 +394,7 @@ code-example(format="." language="javascript").
block hero-list-comp-add-hero
:marked
- Back in the `HeroListComponent`, *its* `addHero()` method subscribes to the observable returned by the *service's* `addHero()` method.
+ Back in the `HeroListComponent`, *its* `addHero()` method subscribes to the Observable returned by the *service's* `addHero()` method.
When the data arrive it pushes the new hero object into its `heroes` array for presentation to the user.
+makeExample('server-communication/ts/src/app/toh/hero-list.component.ts', 'addHero', 'src/app/toh/hero-list.component.ts (addHero)')(format=".")
@@ -375,49 +403,54 @@ block hero-list-comp-add-hero
:marked
Although the Angular `http` client API returns an `Observable` you can turn it into a
[`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
- It's easy to do, and in simple cases, a promise-based version looks much
- like the observable-based version.
+ It's easy to do, and in simple cases, a Promise-based version looks much
+ like the Observable-based version.
.l-sub-section
:marked
- While promises may be more familiar, observables have many advantages.
+ While Promises may be more familiar, Observables have many advantages.
:marked
- Here is a comparison of the `HeroService` using promises versus observables,
+ Here is a comparison of the `HeroService` using Promises versus Observables,
highlighting just the parts that are different.
+makeTabs(
'server-communication/ts/src/app/toh/hero.service.promise.ts,server-communication/ts/src/app/toh/hero.service.ts',
'methods, methods',
'src/app/toh/hero.service.promise.ts (promise-based), src/app/toh/hero.service.ts (observable-based)')
:marked
- You can follow the promise `then(this.extractData).catch(this.handleError)` pattern as in
+ You can follow the Promise `then(this.extractData).catch(this.handleError)` pattern as in
this example.
- Alternatively, you can call `toPromise(success, fail)`. The observable's `map` callback moves to the first *success* parameter and its `catch` callback to the second *fail* parameter in this pattern: `.toPromise(this.extractData, this.handleError)`.
+ Alternatively, you can call `toPromise(success, fail)`. The Observable's `map` callback moves to the
+ first *success* parameter and its `catch` callback to the second *fail* parameter
+ in this pattern: `.toPromise(this.extractData, this.handleError)`.
- The `errorHandler` forwards an error message as a failed promise instead of a failed `observable`.
+ The `errorHandler` forwards an error message as a failed `Promise` instead of a failed `Observable`.
- The diagnostic *log to console* is just one more `then` in the promise chain.
+ The diagnostic *log to console* is just one more `then()` in the Promise chain.
- You have to adjust the calling component to expect a `Promise` instead of an `observable`:
+ You have to adjust the calling component to expect a `Promise` instead of an `Observable`:
+makeTabs(
'server-communication/ts/src/app/toh/hero-list.component.promise.ts, server-communication/ts/src/app/toh/hero-list.component.ts',
'methods, methods',
'src/app/toh/hero-list.component.promise.ts (promise-based), src/app/toh/hero-list.component.ts (observable-based)')
:marked
- The only obvious difference is that you call `then` on the returned promise instead of `subscribe`.
+ The only obvious difference is that you call `then()` on the returned Promise instead of `subscribe`.
Both methods take the same functional arguments.
.l-sub-section
:marked
The less obvious but critical difference is that these two methods return very different results.
- The promise-based `then` returns another promise. You can keep chaining more `then` and `catch` calls, getting a new promise each time.
+ The Promise-based `then()` returns another Promise. You can keep chaining
+ more `then()` and `catch()` calls, getting a new promise each time.
- The `subscribe` method returns a `Subscription`. A `Subscription` is not another `Observable`.
- It's the end of the line for observables. You can't call `map` on it or call `subscribe` again.
+ The `subscribe()` method returns a `Subscription`. A `Subscription` is not another `Observable`.
+ It's the end of the line for Observables. You can't call `map()` on it or call `subscribe()` again.
The `Subscription` object has a different purpose, signified by its primary method, `unsubscribe`.
- To understand the implications and consequences of subscriptions, watch [Ben Lesh's talk on observables](https://www.youtube.com/watch?v=3LKMwkuK0ZE) or his video course on [egghead.io](https://egghead.io/lessons/rxjs-rxjs-observables-vs-promises).
+ To understand the implications and consequences of subscriptions,
+ watch [Ben Lesh's talk on Observables](https://www.youtube.com/watch?v=3LKMwkuK0ZE)
+ or his video course on [egghead.io](https://egghead.io/lessons/rxjs-rxjs-observables-vs-promises).
h2#cors Cross-Origin Requests: Wikipedia example
:marked
@@ -432,7 +465,7 @@ h2#cors Cross-Origin Requests: Wikipedia example
:marked
Modern browsers do allow `XHR` requests to servers from a different origin if the server supports the
[CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) protocol.
- If the server requires user credentials, you'll enable them in the [request headers](#headers).
+ If the server requires user credentials, enable them in the [request headers](#headers).
:marked
Some servers do not support CORS but do support an older, read-only alternative called [JSONP](https://en.wikipedia.org/wiki/JSONP).
@@ -440,8 +473,9 @@ h2#cors Cross-Origin Requests: Wikipedia example
.l-sub-section
:marked
This [Stack Overflow answer](http://stackoverflow.com/questions/2067472/what-is-jsonp-all-about/2067584#2067584) covers many details of JSONP.
+a#search-wikipedia
:marked
- ### Search wikipedia
+ ### Search Wikipedia
Here is a simple search that shows suggestions from Wikipedia as the user
types in a text box:
@@ -453,13 +487,13 @@ block wikipedia-jsonp+
:marked
Wikipedia offers a modern `CORS` API and a legacy `JSONP` search API. This example uses the latter.
The Angular `Jsonp` service both extends the `!{_Http}` service for JSONP and restricts you to `GET` requests.
- All other HTTP methods throw an error because JSONP is a read-only facility.
+ All other HTTP methods throw an error because `JSONP` is a read-only facility.
As always, wrap the interaction with an Angular data access client service inside a dedicated service, here called `WikipediaService`.
+makeExample('server-communication/ts/src/app/wiki/wikipedia.service.ts',null,'src/app/wiki/wikipedia.service.ts')
:marked
- The constructor expects Angular to inject its `jsonp` service, which
+ The constructor expects Angular to inject its `Jsonp` service, which
is available because `JsonpModule` is in the root `@NgModule` `imports` array
in `app.module.ts`.
@@ -486,7 +520,8 @@ block wikipedia-jsonp+
This time you call `jsonp` with *two* arguments: the `wikiUrl` and an options object whose `search` property is the `params` object.
+makeExample('server-communication/ts/src/app/wiki/wikipedia.service.ts','call-jsonp','src/app/wiki/wikipedia.service.ts (call jsonp)')(format=".")
:marked
- `Jsonp` flattens the `params` object into the same query string you saw earlier, putting the request on the wire.
+ `Jsonp` flattens the `params` object into the same query string you saw earlier, sending the request
+ to the server.
:marked
@@ -500,26 +535,27 @@ block wikipedia-jsonp+
and calls a `search(term)` method after each `keyup` event.
The component's `search(term)` method delegates to the `WikipediaService`, which returns an
- observable array of string results (`Observable`).
- Instead of subscribing to the observable inside the component, as in the `HeroListComponent`,
- the app forwards the observable result to the template (via `items`) where the `async` pipe
+ Observable !{_array} of string results (`Observable`).
+ Instead of subscribing to the Observable inside the component, as in the `HeroListComponent`,
+ the app forwards the Observable result to the template (via `items`) where the `async` pipe
in the `ngFor` handles the subscription. Read more about [async pipes](pipes.html#async-pipe)
in the [Pipes](pipes.html) page.
.l-sub-section
:marked
- The [async pipe](pipes.html#async-pipe) is a good choice in read-only components where the component has no need to interact with the data.
+ The [async pipe](pipes.html#async-pipe) is a good choice in read-only components
+ where the component has no need to interact with the data.
`HeroListComponent` can't use the pipe because `addHero()` pushes newly created heroes into the list.
-
+ a#wasteful-app
:marked
## A wasteful app
- The wikipedia search makes too many calls to the server.
- It is inefficient, and potentially expensive on mobile devices with limited data plans.
+ The Wikipedia search makes too many calls to the server.
+ It is inefficient and potentially expensive on mobile devices with limited data plans.
### 1. Wait for the user to stop typing
Presently, the code calls the server after every keystroke.
- It should only make requests when the user *stops typing* .
+ It should only make requests when the user *stops typing*.
Here's how it will work after refactoring:
figure.image-display
img(src='/resources/images/devguide/server-communication/wiki-2.gif' alt="Wikipedia search app (v.2)" width="250")
@@ -544,13 +580,13 @@ block wikipedia-jsonp+
no matter which response arrives first.
- ## More fun with observables
+ ## More fun with Observables
You could make changes to the `WikipediaService`, but for a better
user experience, create a copy of the `WikiComponent` instead and make it smarter,
- with the help of some nifty observable operators.
+ with the help of some nifty Observable operators.
- Here's the `WikiSmartComponent`, shown next to the original `WikiComponent`
+ Here's the `WikiSmartComponent`, shown next to the original `WikiComponent`:
+makeTabs(
`server-communication/ts/src/app/wiki/wiki-smart.component.ts,
@@ -563,21 +599,24 @@ block wikipedia-jsonp+
While the templates are virtually identical,
there's a lot more RxJS in the "smart" version,
starting with `debounceTime`, `distinctUntilChanged`, and `switchMap` operators,
- imported as [described above](#rxjs).
+ imported as [described above](#rxjs-library).
+ a#create-stream
+ :marked
### Create a stream of search terms
The `WikiComponent` passes a new search term directly to the `WikipediaService` after every keystroke.
- The `WikiSmartComponent` class turns the user's keystrokes into an observable _stream of search terms_
+ The `WikiSmartComponent` class turns the user's keystrokes into an Observable _stream of search terms_
with the help of a `Subject`, which you import from RxJS:
+makeExample('server-communication/ts/src/app/wiki/wiki-smart.component.ts', 'import-subject')(format='.')
:marked
The component creates a `searchTermStream` as a `Subject` of type `string`.
- The `search` method adds each new search box value to that stream via the subject's `next` method.
+ The `search()` method adds each new search box value to that stream via the subject's `next()` method.
+makeExample('server-communication/ts/src/app/wiki/wiki-smart.component.ts', 'subject')(format='.')
-
+
+ a#listen-for-search-terms
:marked
### Listen for search terms
@@ -595,11 +634,11 @@ block wikipedia-jsonp+
calls the `WikipediaService` with a fresh, debounced search term and coordinates the stream(s) of service response.
The role of `switchMap` is particularly important.
- The `WikipediaService` returns a separate observable of string arrays (`Observable`) for each search request.
+ The `WikipediaService` returns a separate Observable of string arrays (`Observable`) for each search request.
The user could issue multiple requests before a slow server has had time to reply,
- which means a backlog of response observables could arrive at the client, at any moment, in any order.
+ which means a backlog of response Observables could arrive at the client, at any moment, in any order.
- The `switchMap` returns its own observable that _combines_ all `WikipediaService` response observables,
+ The `switchMap` returns its own Observable that _combines_ all `WikipediaService` response Observables,
re-arranges them in their original request order,
and delivers to subscribers only the most recent search results.
@@ -609,7 +648,7 @@ a#xsrf
## Guarding against Cross-Site Request Forgery
In a cross-site request forgery (CSRF or XSRF), an attacker tricks the user into visiting
- a different web page with malignant code that secretly sends a malicious request to your application's web server,
+ a different web page with malignant code that secretly sends a malicious request to your application's web server.
The server and client application must work together to thwart this attack.
Angular's `Http` client does its part by applying a default `CookieXSRFStrategy` automatically to all requests.
@@ -631,7 +670,7 @@ a#override-default-request-options
before the request is processed.
The `HttpModule` provides these default options via the `RequestOptions` token.
- You can override these defaults to suit your application needs.
+ You can override these defaults to suit your application needs
by creating a custom sub-class of `RequestOptions`
that sets the default options for the application.
@@ -646,7 +685,7 @@ a#override-default-request-options
:marked
Remember to include this provider during setup when unit testing the app's HTTP services.
:marked
- After this change, the `header` option setting in `HeroService.addHero` is no longer necessary,
+ After this change, the `header` option setting in `HeroService.addHero()` is no longer necessary,
+makeExample('server-communication/ts/src/app/toh/hero.service.ts', 'addhero', 'src/app/toh/hero.service.ts (addHero)')(format=".")
:marked
@@ -709,7 +748,7 @@ block redirect-to-web-api
:marked
Finally, redirect client HTTP requests to the in-memory web API by
adding the `InMemoryWebApiModule` to the `AppModule.imports` list.
- At the same time, call its `forRoot` configuration method with the `HeroData` class.
+ At the same time, call its `forRoot()` configuration method with the `HeroData` class.
+makeExample('server-communication/ts/src/app/app.module.ts', 'in-mem-web-api', 'src/app/app.module.ts')(format=".")
:marked
### How it works
@@ -722,7 +761,7 @@ block redirect-to-web-api
At the same time, the `forRoot` method initializes the in-memory web API with the *seed data* from the mock hero dataset.
.l-sub-section
:marked
- The `forRoot` method name is a strong reminder that you should only call the `InMemoryWebApiModule` _once_,
+ The `forRoot()` method name is a strong reminder that you should only call the `InMemoryWebApiModule` _once_,
while setting the metadata for the root `AppModule`. Don't call it again.
:marked
Here is the final, revised version of src/app/app.module.ts, demonstrating these steps.