MaPta
MaPta

Reputation: 71

Angular 9 <- how to subscribe to Observable and display its values

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?

Upvotes: 0

Views: 4633

Answers (2)

Barremian
Barremian

Reputation: 31105

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.

Upvotes: 2

Andrei Gătej
Andrei Gătej

Reputation: 11934

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.

Upvotes: 1

Related Questions