0

Dears,

I am stuck, i have followed simple angular.io tutorial for http.get and observables, but i am not able to render the data in the view.

My service:

import { Injectable } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { Observable } from "rxjs";
import { map } from "rxjs/operators";

const baseUrl = "http://localhost:4000";
const fetchAllHeroesUrl = "/heroes";
@Injectable({
  providedIn: "root",
})
export class HeroesService {
  constructor(private http: HttpClient) {}
  getHeroes(): Observable<any> {
    return this.http.get(baseUrl + fetchAllHeroesUrl);
  }
}

My component:

import { Component, OnInit } from "@angular/core";
import { HeroesService } from "./../shared/services/heroes.service";
import { Observable } from "rxjs";
@Component({
  selector: "app-hero-list",
  templateUrl: "./hero-list.component.html",
  styleUrls: ["./hero-list.component.scss"],
})
export class HeroListComponent implements OnInit {
  public listOfHeroes$: any;
  constructor(private heroesService: HeroesService) {}

   ngOnInit() {
     this.getAllHeroes();
  }
  getAllHeroes(): void {
   this.heroesService
      .getHeroes().subscribe((value: any) =>  this.listOfHeroes$ = value.data);
  }
}

my template:

<mat-list>
    <mat-list-item *ngFor="let hero of listOfHeroes$ | async">
        <img
            matListAvatar
            src="hero.avatar_url"
            alt="hero.full_name">
        <h3 matLine>{{hero.full_name}}</h3>
        <p matLine>
            <span>{{hero.description}}</span>
            <span class="demo-2">-- {{hero.type}}</span>
        </p>
    </mat-list-item>
</mat-list>

but still nothing is showing in the view (in webbrowser, also i have checked Network tab in dev tools, and the data come from my local erver), whenever i console.log(this.listOfHeroes$) i get undefined Shouldn't this be working? am i missing something?

MaPta
  • 71
  • 6

2 Answers2

2

You are both subscribing in the controller and using the async pipe. It should be either of the two. Both has it's advantages and disadvantages. In this scenario, I'd recommend you to do the following

Remove getAllHeroes() function altogether and modify the controller

export class HeroListComponent implements OnInit {
  public listOfHeroes$: any;
  constructor(private heroesService: HeroesService) {}

   ngOnInit() {
     this.listOfHeroes$ = this.heroesService.getHeroes();
  }
}

And in the template

<ng-container *ngIf="listOfHeroes$ | async as list">
  <mat-list-item *ngFor="let hero of list?.data">
    <img
      matListAvatar
      src="hero.avatar_url"
      alt="hero.full_name">
    <h3 matLine>{{hero.full_name}}</h3>
    <p matLine>
      <span>{{hero.description}}</span>
      <span class="demo-2">-- {{hero.type}}</span>
    </p>
  </mat-list-item>
<ng-container>

One reason why I prefer async here is because it takes care of the unsubscribing from the HTTP GET to avoid potential memory leak issues.

Michael D
  • 20,838
  • 4
  • 12
  • 37
  • Unfortunatly this did not help. Also issue is that the response from server is in form: {data:[...], total:12} - when i use above solution then i get error: that ngFor does not support Object,Object etc. only arrays – MaPta May 17 '20 at 12:49
  • 1
    So the array is enclosed in the `data` property. You could wrap the `async` pipe in `ng-container` tag and loop over the array by accessing the `data` property. The `ng-container` element isn't included in the DOM. It will only be included as a comment. I've modified the answer. Please see if it works. – Michael D May 17 '20 at 12:55
  • i wanted to do the mapping on service side, like: pipe(map(res => res = res.data)) but angular 9 says it does not recognize map – MaPta May 17 '20 at 13:44
  • Map is part of RxJS. You need to import it: `import { map } from 'rxjs/operators';` – Michael D May 17 '20 at 13:46
  • i did so, then VS code said: that map is not an export member of rxjs, but i guess i did something wrong, because now it works, :) and i do not need to use ng-container. Just one short question: if i had the method, then inside i have assigned value to a global variable, then shouldn't it be available outside the method? – MaPta May 17 '20 at 13:53
  • Yes it should be available, but it is asynchronous data. It will be assigned the value as soon the function is called, it will be done sometime later. Please see here for more info on asynchronous data: https://stackoverflow.com/a/14220323/6513921 – Michael D May 17 '20 at 14:27
1

The async pipe needs to be given a promise or an observable.

You could do this:

this.listOfHeroes$ = this.heroesService.getHeroes();

as you can see, there is no need to subscribe in your component. the async pipe will handle the subscription & unsubscription for you.

Andrei Gătej
  • 8,356
  • 1
  • 7
  • 24