manzapanza
manzapanza

Reputation: 6225

Pass data from server side to client side Angular Universal

There is a way to define constants values (like cached data), during the the server side execution, and be available on the client side execution too?

It could works like a cache system, a cache that is generated when the app runs on server side.

For example I have a categories list returned by my api resource api/categories. I would like store the result of this request only on the first call of my app when runs on the server side, and this cached data be available on the client side without request another time api/categories. How can I do that?

I have already implemented BrowserModule.withServerTransition to transfer the state from server to client, it works, but sincerely I don't understand how it actually works.

So.. how can I pass arbitrary data from server side to client side execution of my angular app?

Upvotes: 2

Views: 2104

Answers (1)

Marcel
Marcel

Reputation: 391

Create a new TransferHttpInterceptorService:

import {Injectable, Inject, PLATFORM_ID} from '@angular/core';
import {HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpResponse} from '@angular/common/http';
import {Observable, of} from 'rxjs';
import {tap} from 'rxjs/operators';
import {TransferState, makeStateKey, StateKey} from '@angular/platform-browser';
import {isPlatformServer} from '@angular/common';

@Injectable({
  providedIn: 'root'
})
export class TransferHttpInterceptorService implements HttpInterceptor {
  constructor(
    private transferState: TransferState,
    @Inject(PLATFORM_ID) private platformId: any) {
  }

  public intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    if (request.method !== 'GET') {
      return next.handle(request);
    }

    const key: StateKey<string> = makeStateKey<string>(request.url);

    if (isPlatformServer(this.platformId)) {
      return next.handle(request).pipe(tap((event) => {
        this.transferState.set(key, (<HttpResponse<any>>event).body);
      }));
    } else {
      const storedResponse = this.transferState.get<any>(key, null);
      if (storedResponse) {
        const response = new HttpResponse({body: storedResponse, status: 200});
        this.transferState.remove(key);
        return of(response);
      } else {
        return next.handle(request);
      }
    }
  }
}

Note here: If you have use a different host on your server or use an relative path, you need to change the key to

const key: StateKey<string> = makeStateKey<string>(request.url.split("/api").pop());

This might not the cleanest solution, but it works.

Add BrowserTransferStateModule and TransferHttpInterceptorService from above to app.module.ts

imports: [
    BrowserModule.withServerTransition({appId: 'serverApp'}),
    BrowserTransferStateModule, <--
    ...
]
providers: [
{
  provide: HTTP_INTERCEPTORS,
  useClass: TransferHttpInterceptorService,
  multi: true
},
...
]

Upvotes: 2

Related Questions