Tutoriels Backend
Transfer state with angular
The httpclient module associated with the Side Rendering Server ( SSR) generates two requests during an API call.
Which doubles the workload on a backend.
So the goal of this tutorial is to improve the SSR of our Angular application.
We will add two modules ServerTransferStateModule and BrowserTransferStateModule to our project.
We will use the Angular javascript framework.

What are we going to do?
Important note.
This feature is no longer useful on Angular 15 as the Google team has fixed the bug.
So this tutorial is only useful with Angular 14 or earlier versions.
This is the step in ourAngular guide that will allow us to obtain a PWA type Web Application .
The basic Angular project we will be using already has the following features
- Generated with Angular CLI
- The Routing
- Lazy Loading
- The Bootstrap CSS Framework
- Server Side Rendering
- HttpClient
All the sources created are indicated at the end of the tutorial.
The application is at the following address
Verification
First, let's check the theory.
The code we are going to analyze is located in the items.component.ts file
For this we will make some modifications to this file .
import { Component, OnInit } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
import { PLATFORM_ID, APP_ID, Inject } from '@angular/core';
import { ItemsService } from './items.service';
@Component({
selector: 'app-items',
templateUrl: './items.component.html',
styleUrls: ['./items.component.css']
})
export class ItemsComponent implements OnInit {
items: any;
loaded: boolean;
constructor(
private itemsService: ItemsService,
@Inject(PLATFORM_ID) private platformId: Object,
@Inject(APP_ID) private appId: string) {
this.loaded = false;
}
ngOnInit(): void {
this.getUsers();
}
getUsers(): void {
this.itemsService.getItems('https://jsonplaceholder.typicode.com/users')
.subscribe(
items => {
const platform = isPlatformBrowser(this.platformId) ?
'in the browser' : 'on the server';
console.log(`getUsers : Running ${platform} with appId=${this.appId}`);
this.loaded = true;
this.items = items;
});
}
resetUsers(): void {
this.items = null;
this.loaded = true;
}
}
Now we will test this theory.
Case No. 1 (without SSR)
- npm run start
- Launch Chrome
- Activate developer tools with Ctrl + Shift + J
- Launch http://localhost:4200/httpclient
- in chrome console check a request call in browser
- getUsers: Running in the browser with appId=angular-starter
Case #2 (with SSR)
- npm run build:ssr
- npm run serve:ssr
- Launch Chrome
- Activate developer tools with Ctrl + Shift + J
- Launch http://localhost:4000/httpclient
- in chrome console check a request call in browser
- getUsers: Running in the browser with appId=angular-starter
- In the launch console of the prompt check a request call in the server
- Node server listening on http://localhost:4000
- getUsers: Running on the server with appId=angular-starter
Modification
The solution is to use two modules from Angular.
ServerTransferStateModule and BrowserTransferStateModule .
To do this we need to modify some of our files.
The steps are as follows.
- Edit main.ts
- Create src/app/ app.browser.module.ts
- Edit app.server.module.ts
- Edit app.module.ts (add CommonModule)
- Edit items.component.ts
- Edit items.component.spec.ts
- Edit tslint.json
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppBrowserModule } from './app/app.browser.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
function bootstrap() {
platformBrowserDynamic().bootstrapModule(AppBrowserModule)
.catch(err => console.error(err));
};
if (document.readyState === 'complete') {
bootstrap();
} else {
document.addEventListener('DOMContentLoaded', bootstrap);
}
import { NgModule } from '@angular/core';
import { BrowserModule, BrowserTransferStateModule } from '@angular/platform-browser';
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
@NgModule({
imports: [
AppModule,
BrowserModule.withServerTransition({ appId: 'angular-starter' }),
BrowserTransferStateModule
],
bootstrap: [AppComponent],
})
export class AppBrowserModule { }
import { NgModule } from '@angular/core';
import { ServerModule, ServerTransferStateModule } from '@angular/platform-server';
import { BrowserModule } from '@angular/platform-browser';
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
@NgModule({
imports: [
AppModule,
ServerModule,
ServerTransferStateModule,
BrowserModule.withServerTransition({ appId: 'angular-starter' }),
],
bootstrap: [AppComponent],
})
export class AppServerModule { }
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { HomeComponent } from './modules/general/home/home.component';
import { NotFoundComponent } from './modules/general/not-found/not-found.component';
import { AppRoutingModule } from './app-routing.module';
import { HttpClientModule } from '@angular/common/http';
@NgModule({
declarations: [
AppComponent,
HomeComponent,
NotFoundComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
import { Component, OnInit } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
import { TransferState, makeStateKey } from '@angular/platform-browser';
import { PLATFORM_ID, APP_ID, Inject } from '@angular/core';
import { ItemsService } from './items.service';
const STATE_KEY_ITEMS = makeStateKey('items');
@Component({
selector: 'app-items',
templateUrl: './items.component.html',
styleUrls: ['./items.component.css']
})
export class ItemsComponent implements OnInit {
// items: any;
items: any = [];
loaded: boolean;
constructor(
private state: TransferState,
private itemsService: ItemsService,
@Inject(PLATFORM_ID) private platformId: object,
@Inject(APP_ID) private appId: string) {
this.loaded = false;
}
ngOnInit(): void {
this.getUsers();
}
getUsers(): void {
this.loaded = false;
this.items = this.state.get(STATE_KEY_ITEMS, <any> []);
if (this.items.length === 0) {
this.itemsService.getItems('https://jsonplaceholder.typicode.com/users')
.subscribe(
items => {
const platform = isPlatformBrowser(this.platformId) ?
'in the browser' : 'on the server';
console.log(`getUsers : Running ${platform} with appId=${this.appId}`);
this.items = items;
this.loaded = true;
this.state.set(STATE_KEY_ITEMS, <any> items);
});
} else {
this.loaded = true;
}
}
resetUsers(): void {
this.items = null;
this.loaded = true;
}
}
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HttpClientModule } from '@angular/common/http';
import { ItemsComponent } from './items.component';
import { BrowserTransferStateModule } from '@angular/platform-browser';
describe('ItemsComponent', () => {
let component: ItemsComponent;
let fixture: ComponentFixture<ItemsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
HttpClientModule,
BrowserTransferStateModule
],
declarations: [ItemsComponent]
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ItemsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
Conclusion
Perform the previous test to see that there is only one API call left on the server.
- npm run build:ssr
- npm run server:ssr
- localhost:4000/httpclient
All that remains is to test the various Angular scripts to finalize the application.
# Développement
npm run start
http://localhost:4200/
# Tests
npm run lint
npm run test
npm run e2e
# AOT compilation
npm run build
# SSR compilation
npm run build:ssr
npm run serve:ssr
http://localhost:4000/
Code source
The source code used at the beginning of the tutorial is available on github
https://github.com/ganatan/angular-httpclient
The source code obtained at the end of this tutorial is available on github
https://github.com/ganatan/angular-transferstate
The following steps will get you a prototype application
- Step 1: Getting Started with Angular
- Step 2: Routing with Angular
- Step 3: Lazy loading with Angular
- Step 4: Bootstrap with Angular
- Step 5: Modules with Angular
- Step 6: Components with Angular
- Step 7: Services with Angular
- Step 8: Template Driven Forms with Angular
- Step 9: Charts with Angular
- Step 10: Server Side Rendering with Angular
- Step 11: HttpClient with Angular
- Step 12: Transfer State with Angular
- Step 13: Progressive Web App with Angular
- Step 14: Search Engine Optimization with Angular
The last step is to get a sample application
The source code for this final application is available on GitHub
https://github.com/ganatan/angular-app
How to create a From scratch application?
Create your ganatan account
Download your complete guides for free
Démarrez avec angular CLI 
Gérez le routing 
Appliquez le Lazy loading 
Intégrez Bootstrap 
Utilisez Python avec Angular 
Utilisez Django avec Angular 
Utilisez Flask avec Angular 
