18

I have set up and used successfully Angular5 + SSR. It is still pretty nice.

All components work well on SSR and Non-SSR. And there are some services which call external HTTP get APIs to get some data. Of course, it works well on a Non-SSR mode.

But, the problem is that on SSR, the node server does not support to fetch and render the data. I can only see the fetched data after client-side fetching and rendering.

import {Injectable} from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import 'rxjs/add/operator/map';
import {Observable} from "rxjs";

const httpOptions = {
    headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};

@Injectable()
export class BannerService {

    constructor(public http: HttpClient){
        console.log('SmsService Initialized...');
    }

    getBanners(){
        return this.http.get(BASE_API_URL + '/getData', httpOptions);
    }
}

home.component.ts

import {Component, OnDestroy, OnInit} from '@angular/core';
import { Router } from "@angular/router";
import {Subscription} from "rxjs/Subscription";
import {BannerService} from "../../services/banner.service";

@Component({
    selector: 'app-home',
    styleUrls: ['home.container.css'],
    templateUrl: 'home.container.html'
})

export class HomeContainerComponent implements OnInit, OnDestroy {

    public horizontalBannerList1;
    public horizontalBannerList2;
    public verticalBannerList;
    private bannerList;

    constructor( private router: Router, private bannerService: BannerService){
         ...
    }

    ngOnInit() {
        this.initBannerList();
    }

    ngOnDestroy() {
       ...
    }

    initBannerList(){

        if(this.bannerList){
            return;
        }

        this.bannerService.getBanners().subscribe(
            result => {
                console.log("bannerList result : ", result);
                this.bannerList = result;    
            },
            error => {
                console.error("bannerList error: ", error);
            },
            () => {
                console.log("bannerList completed");
            });
    }

}

I expected that on SSR the node server calls HTTP request data and render it on index.html but it's not...

Am I missing or misunderstood?

ps : The same issues are reported. https://github.com/angular/universal/issues/674 If I solve these issues or find out the good doc, I would update it again. :)

8
  • Do you get any error server side? How did you check that the API is not called? Can you should more code (e.g. where/how getBanners is called) Commented Mar 19, 2018 at 8:35
  • @David I added home component source. it work well in client rendering. And there is no error in SSR mode. I made an another api server and it makes logs whenever the function is called. It is only called in client rendering, not SSR. Commented Mar 19, 2018 at 23:58
  • It that component called from a lazy loaded route? And it the console log in the service constructor called server side? Commented Mar 20, 2018 at 8:19
  • @David I don't use lazy loaded route.. It is so strange :( Commented Mar 20, 2018 at 23:45
  • 1
    Pure SSR doesn't include "fetching data on server side". React.js Native SSR doesn't support either. But there is another framework: 'next.js' (only for react.js); 'nuxt.js'(only for vue.js) to implement the feature. Commented Mar 17, 2019 at 1:18

2 Answers 2

1

Angular Universal’s TransferState allows you to share the fetched data between the server and client.

you can use it like this

banner.service.ts:

import { Injectable, Inject, PLATFORM_ID } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { makeStateKey, TransferState } from '@angular/platform-browser';
import { isPlatformServer } from '@angular/common';
import { Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';

const httpOptions = {
  headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
};
const BANNERS_KEY = makeStateKey<any>('banners');

@Injectable()
export class BannerService {
  constructor(
    private http: HttpClient,
    private transferState: TransferState,
    @Inject(PLATFORM_ID) private platformId: Object
  ) {}

  getBanners(): Observable<any> {
    if (this.transferState.hasKey(BANNERS_KEY)) {
      return new Observable((observer) => {
        // Use cached data if it exists
        observer.next(this.transferState.get(BANNERS_KEY, null));
        this.transferState.remove(BANNERS_KEY); // Clean up after SSR
        observer.complete();
      });
    } else if (isPlatformServer(this.platformId)) {
      // Server-side data fetching
      return this.http.get(BASE_API_URL + '/getData', httpOptions).pipe(
        tap((data) => {
          this.transferState.set(BANNERS_KEY, data);
        })
      );
    } else {
      // Client-side data fetching
      return this.http.get(BASE_API_URL + '/getData', httpOptions);
    }
  }
}

and home.component.ts will be like:

import { Component, OnDestroy, OnInit } from '@angular/core';
import { BannerService } from '../../services/banner.service';

@Component({
  selector: 'app-home',
  styleUrls: ['home.container.css'],
  templateUrl: 'home.container.html',
})
export class HomeContainerComponent implements OnInit, OnDestroy {
  public horizontalBannerList1: any;
  public horizontalBannerList2: any;
  public verticalBannerList: any;
  private bannerList: any;

  constructor(private bannerService: BannerService) {}

  ngOnInit() {
    this.initBannerList();
  }

  ngOnDestroy() {}

  initBannerList() {
    if (this.bannerList) {
      return;
    }

    this.bannerService.getBanners().subscribe(
      (result) => {
        console.log('bannerList result : ', result);
        this.bannerList = result;
        // Further process the banners as required
      },
      (error) => {
        console.error('bannerList error: ', error);
      },
      () => {
        console.log('bannerList completed');
      }
    );
  }
}

Sign up to request clarification or add additional context in comments.

Comments

0

You need to check using Angular Universal features like resolvers or the TransferState API to pre-fetch data You can also optimize API response to SSR, Conditional rendering can also be used to display loading indicators during SSR while fetching data asynchronously on the client side.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.