Using TransferState API in an Angular v5 Universal App
Let’s illustrate the article with a concrete example. We have a weather application that displays a list of cities in its sidebar. When you click on a city name, the application displays the current weather for that city.
Because we wanted our application to be crawlable and indexable, we made it universal: the city pages are rendered on the server, stored as HTML files and served by an HTTP server. These pages will contain browser applications, so users can continue to navigate through the application using the power of Angular after the first page loads.
You can follow these steps to try out this simple example.
Clone the example locally using the following command:
$ git clone https://github.com/feloy/ng-demo-transfer-state
$ cd ng-demo-transfer-state
$ git checkout initial
Copy the code
Builder:
$ npm install
$ ng build -prod
$ ng build -prod -app server --output-hashing=none
Copy the code
Create different pages for different cities:
$ node render-page.js /Paris > dist/Paris
$ node render-page.js /London > dist/London
$ node render-page.js /San%20Fransisco > 'dist/San Fransisco'
Copy the code
You can now service the Dist directory using your preferred HTTP server.
Now, if you go directly to the page http://your-domain/Paris (which is typical for visitors coming from a search engine), you can observe the page flicker – because the content already exists and has been downloaded locally, and the browser application reloads and displays it again.
TransferState to the rescue
The TransferState API introduced in Angular V5 can help resolve this situation. It can transfer data from the server side of the application to the browser application. To do this, the server application will add the data we want to transfer to the HTML page it generates. The browser application included in the generated HTML page will be able to read this data.
View solutions in this branch.
$ git checkout transfer-data
Copy the code
First import ServerTransferStateModule on application server, the browser application on import BrowserTransferStateModule:
// src/app/app.server.module.ts
import {ServerTransferStateModule} from '@angular/platform-server'; [...].@NgModule({
imports: [
AppModule,
ServerModule,
ServerTransferStateModule
],
bootstrap: [AppComponent],
})
export class AppServerModule {}
// src/app/app.module.ts
import { BrowserTransferStateModule } from '@angular/platform-browser'; [...].@NgModule({
declarations: [
AppComponent, CityComponent
],
imports: [
BrowserModule.withServerTransition({ appId: 'ng-demo-transfer-state-app' }),
BrowserTransferStateModule,
[...]
Copy the code
Now we can use the TransferState API in the parser that supplies the data to the component:
- On the server, we first register onSerialize to provide the data we will download, and then we GET the data from our data provider, in this case an HTTP GET request.
- On the browser, we use the GET method to get the data provided by the server, which we provide directly. We also removed the supplied data from the transport state, so the page reload will no longer use the supplied data.
We can detect whether we are on a server or a browser application by calling the hasKey method. This method returns true only in the browser.
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<CityWeather> {
const found = this.transferState.hasKey(RESULT_KEY);
if (found) {
const res = Observable.of(this.transferState.get<CityWeather>(RESULT_KEY, null));
this.transferState.remove(RESULT_KEY);
return res;
} else {
this.transferState.onSerialize(RESULT_KEY, () = > this.result);
const name = route.params['city'];
return this.http.get<CityWeather>('https://api.openweathermap.org/data/2.5/weather?q=' + name + '&units=metric&APPID=' + this.key)
.do(result= > this.result = result); }}Copy the code
Since we are calling the remove method to remove the provided data, the following page displayed by the browser calls onSerialize, but this method has no effect because toJson is only called on the server side.
A clearer solution is to use the isPlatformServer and isPlatformBrowser methods to detect the platform and take action accordingly.
For more of Jerry’s original articles, see “Wang Zixi “: