4

I'm trying to make Angular wait for the API response but I'm not having success.

I used the TransferState, it works, but the result doesnt appear in the View Source. Angular still flickers to show the response after the View is loaded.

I used the resolve parameter on the route module but it is the same thing.

I'm using the Universal Starter https://github.com/angular/universal-starter

I used test API like reqres.in and https://jsonplaceholder.typicode.com/ and they work. Both show in the view source but with my API made with Laravel 5 it doesn't and I don't know why. The response in ms of my API is even faster than the jsonplaceholder but still, the ngFor that I do to the response (which is an array) still doesnt show in the View Source.

I have this on my service.ts

import { environment } from './../../../environments/environment';
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class NestedService {

  // ----------  Headers -----------------
  public headers: HttpHeaders = new HttpHeaders({
    Accept: 'application/json',
    'Content-Type': 'application/json'
  });

  constructor(
    private http: HttpClient
  ) { }

  public getReq() {
    return this.http.get(environment.REQRES);
  }

  public mostrarProductos() {
    return this.http.get('https://jsonplaceholder.typicode.com/posts');
  }

  public julienne() {
    return this.http.get('https://juliennecosmetic.com/backend/api/products?category=3');
  }
}

I have this on my component.ts

import { NestedService } from './../services/nested.service';
import { Component, OnInit } from '@angular/core';
import { makeStateKey, TransferState } from '@angular/platform-browser';
import { ActivatedRoute } from '@angular/router';

const USERS_KEY = makeStateKey('products');
const PRODUCTS = makeStateKey('products2');
const JULIENNE = makeStateKey('products3');

@Component({
  selector: 'app-nested',
  template: `
    <p>
      nested worksa asfd safdsa!
    </p>

    <div class="row">
    <div class="col-sm-6 col-md-4 col-lg-3 p-2 mx-auto bg-info" *ngFor="let user of julienne">
    <p>{{user.nombre_comercial}}</p>
    </div>
    </div>

    <div class="row">
    <div class="col-sm-6 col-md-4 col-lg-3 p-2 mx-auto" *ngFor="let user of users">
      {{user.email}}
    </div>
    </div>

    <div class="row">
    <div class="col-sm-6 col-md-4 col-lg-3 p-2 mx-auto" *ngFor="let user of productos">
      <p>{{user.id}}</p>
      <p>{{user.body}}</p>
      <p>{{user.title}}</p>
    </div>
    </div>

  `,
  styles: []
})
export class NestedComponent implements OnInit {
  users;
  productos;
  julienne;

  constructor(
    private nested: NestedService,
    private state: TransferState,
    private aRoute: ActivatedRoute
  ) {}

  ngOnInit() {
    this.users = this.state.get(USERS_KEY, null);
    this.productos = this.state.get(PRODUCTS, null);
    this.julienne = this.state.get(JULIENNE, null);

    if (!this.julienne) {
      this.nested.julienne().subscribe(data => {
        console.log(data);
        this.julienne = data['response']['data'];
        this.state.set(JULIENNE, data['response']['data']);
      });
    }

    if (!this.users) {
      this.nested.getReq().subscribe(data => {
        console.log(data);

        this.users = data['data'];
        this.state.set(USERS_KEY, data['data']);
      });
    }

    if (!this.productos) {
      this.nested.mostrarProductos().subscribe(data => {
        console.log(data);

        this.productos = data;
        this.state.set(PRODUCTS, data);
      });
    }
  }
}

In this tool, it only shows the https://jsonplaceholder.typicode.com/ and reqres list generated, but not the 3rd one on juliennecosmetic.com

https://search.google.com/structured-data/testing-tool#url=http%3A%2F%2F31.220.57.15%2Flazy%2Fnested

What it could be?

Jeannuel
  • 71
  • 5

3 Answers3

2

You are effectively allowing your component to iterate over a non-existent list until the API has responded.

You can handle it in a couple of ways it all depends on the ux needs.

  1. Wrap the whole thing in a condition i.e.
<div *ngIf="users">
... *ngFor="let user of users ...
</div>
  1. Provide empty lists to start with so the *ngFor has something to iterate over but just no content.

  2. Switch a boolean flag indicating the download phase is done and the UI can now render, but again this involves not reaching the *ngFor logic until you have at least an empty list to go over.

P.S. This is your issue :)

I'm trying to make Angular wait for the API response but I'm not having success.

Forget about everything you know when it comes to waiting for things to happen sequentially, you need to accommodate for the initial state in some other way until something has happened, i.e. by either not showing the table, showing a spinner or even not loading the component at all.

2

Perhaps you should try to work with Observables or Promises and the Angular async pipe. Also as that is an async request, it is normal that the component may be rendered before getting data from the server, so that, you can add a loader.

You can dive a bit into the HttpInterceptor documentation to implement a loader. You can find a good example of it in this article

0

Thanks for the comments.

The API that didnt work, was the 'juliennecosmetic' one. That API was hosted in GoDaddy on a Shared Server, it worked to retrieve the data but for some reason I still I had this problem with Google.

The fix for me was changing the API from a shared server to a VPS, I tried everything on the GoDaddy Shared Server side but didnt work, I changed to a Ubuntu VPS and it worked like the reqres and jsonplaceholder.

Jeannuel
  • 71
  • 5