famas23
famas23

Reputation: 2290

Consume API via http

There are three ways to consume an API resource, 3 approaches, that can solve the situation.
Except, that I really do not know what are the cases where we would use each of these 3 mehtods. However, here are the three below.
In wish case we need to follow such method ?

First method:
It's creating an observable this.articles$ = this.http.get(this._articlesUrl); then we make *ngIf="!(articles$ | async)" then *ngFor="let item of articles$ | async; trabckedBy: trackElement"

export class ArticleComponent implements OnInit {

  private _articlesUrl = 'http://127.0.0.1:8000/api/articles';
  private articles$: Observable<any>;

  constructor(
    private http: HttpClient,
  ) { }

  ngOnInit() {
    this.articles$ = this.http.get(this._articlesUrl)
      .pipe(share());
  }

  trackElement(index: number, element: any) {
    return element ? element.id : null;
  }

Second method:

export class ArticleComponent implements OnInit {

  private _articlesUrl = 'http://127.0.0.1:8000/api/articles';
  private articles$: Observable<any>;

  constructor(
    private http: HttpClient,
  ) { }

  ngOnInit() {
    this.articles$ = this.http..get(this._articlesUrl ).pipe(map(data => data.articles)); // I dony know this syntaxe
  }

  trackElement(index: number, element: any) {
    return element ? element.id : null;
  }

Then we make *ngIf="!(articles$ | async)" then *ngFor="let item of articles$ | async; trabckedBy: trackElement"

Third method:

export class ArticleComponent implements OnInit, OnDestroy {

  private _articlesUrl = 'http://127.0.0.1:8000/api/articles';
  private subscription: Subscription;

  constructor(
    private http: HttpClient,
  ) { }

  ngOnInit() {
    this.subscription = this.http.get(this._articlesUrl).subscribe(
      (data) => {
        this.articles = data.articles;
      }
    );
  }

  trackElement(index: number, element: any) {
    return element ? element.id : null;
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

The next step is to *ngIf="!articles" then *ngFor="let item of articles; trabckedBy: trackElement" to parse the articles.

Upvotes: 1

Views: 102

Answers (2)

Eliseo
Eliseo

Reputation: 58039

@Ahmedbhs, when you has an observable, you can

1.-use direct in the .html using | async.

<div *ngFor="let item of articles$ | async;>...</div>

articles$:Observable<any>  //<--declare your variable
this.articles$=this.httpClient.get("your url")

See that you equal this.articles$ to this.httpClient (to an Observable)

2.-Subscribe and use a variable

<div *ngFor="let item of articlesData>...</div>

articlesData:any[];  //<--declare your variable
this.httpClient.get("your url").subscribe(res=>this.articlesData)

See that you subscribe to this.httpClient (to an Observable) and when you have the result (into function subscribe) you equal a variable "this.articlesData" the result of the call. So you iterate over an array of data

When you subscribe to data, is "good practice" unsubscribe. In the first example the pipe async make it for you. In the second example you can use a variable that was true and in ngOnAfter equal false

articlesData:any[];  //<--declare your variable
live:boolean=true;   //<---declare a variable "live"
this.httpClient.get("your url").pipe(
      takeWhile(()=>this.live==true)
      ).subscribe(res=>this.articlesData) 

ngOnAfterInit()
{
    this.live=false;
}

Upvotes: 1

JB Nizet
JB Nizet

Reputation: 692181

None of them is fine.

First of all, the best and common practice is to put the HTTP-related code in a service, not in the component directly. That makes the code of the component simpler, and easier to test.

First one: you didn't say why you're using share() in the service. I assume it is because you noticed that using the async pipe twice in the view sends two http requests. But it's not the job of the service to take care of fixing your view, so you should not do that.

Second one: you don't seem to understand what the map() operator does. It simply transform the events emitted by the source observable into something else. In this case, it transforms objects of the form { articles: [...] } into [...]. It doesn't share the observable at all. You'd need to do that if the service sends back an object, and you're only interested in the articles array of that object. Since you're using async twice in the view, you'll send the http request twice, which is not something you want to do.

Third one: it's correct, but storing the subscription and unsubscribing on destroy is generally useless: the observable completes as soon as the response has come back, and when it completes (or errors), subscribers are automatically unsubscribed. That would be useful if you subscribed to a very long-running observable (like an interval, which never completes).

So I would use the third one, but remove the unnecessary code.

If you really want to use the async pipe, then make sure to use it once and only once in the view:

<div *ngIf="articles$ | async as articles; else loading">
  Here's the list of articles:
  <div *ngFor="let article of articles"> ... </div>
</div>
<ng-template #loading>
  Loading articles...
</ng-template>

Upvotes: 2

Related Questions