hunB
hunB

Reputation: 331

how to make HTTP call only once on component creation

I am retrieving data from a web api through angular 6.

I created a service with a fetch function that is called in a component constructor, which will be instanciated multiple times on the same page.

My problem is that the service is called each time my component is instanciated.

For example my code is the following :

Service :

import { Injectable } from '@angular/core';
import { Article } from '../../Classes/article'
import { Observable } from 'rxjs';
import { MessageService } from '../../Services/message/message.service'
import { HttpClient } from '@angular/common/http'


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

  Values:  Observable<Article[]> ;


  constructor(private messageService: MessageService, private http : HttpClient) 
  { 
 this.buildArticles()
  }

private articlesUrl = 'http://localhost:63138/v/Article'

private buildArticles():  void {
    this.Values =  this.http.get<Article[]>(this.articlesUrl)
}

getArticles() : Observable<Article[]> {
return this.Values
  }
}

My component :

import { Component,OnInit, Input } from '@angular/core';
import { ArticleService } from 'src/app/Services/article/article.service';
import { Article } from 'src/app/Classes/Article'

@Component({
  selector: 'app-article',
  templateUrl: './article.component.html',
  styleUrls: ['./article.component.scss'],
})

export class ArticleComponent implements OnInit {

  @Input() artId  :number;


  constructor( private articleService : ArticleService) { 
    articleService.getArticles().subscribe(x=> this.articles = x);
   }
  articles : Article[];
  ngOnInit(): void {
  }
}

with the component html :

<mat-form-field>
  <mat-select [(ngModel)]="artId" >
    <mat-option *ngFor="let art of articles" [value]="art.Id"> 
 {{art.Libelle1}}</mat-option>
  </mat-select>
</mat-form-field>

Now on my app I would like the service fetch method to be called once, and reuse the data each time I instanciate a new component.

In the app.module.ts, I put providers: [ArticleService] in @NgModule but I get a request to my web api for each instance of the component.

Upvotes: 2

Views: 10112

Answers (2)

user4676340
user4676340

Reputation:

Angular creates components on demand.

In your case, everytime a component is created, you make an HTTP call by using the service method to fetch your data.

The issue isn't with instances of the service (or even your component), it's the design you have chosen that doesn't suit your case.

What you need is some sort of caching. To implement that, you can use subjects :

private _cachedData: Subject<any>;
public data = this._cachedData.asObservable();

refreshData() {
  this.http.get(...).subscribe(res => this._cachedData.next(res));
}

initializeData() {
  if (!this._cachedData) {
    this._cachedData = ,new Subject();
    this.refreshData();
  }
}

This is very simple, but it should do the trick. Now you can just use myService.data.subscribe() to get the cached data, and you can initialize data in your component constructor (because your service is a singleton, if you try to call it with data already cached, the service will simply ignore the request).

Upvotes: 6

piotr szybicki
piotr szybicki

Reputation: 1602

By declaring your component

@Injectable({
  providedIn: 'root'
})

you guarantee that it will be one instance of your service, the problem is with the fact that every time you call subscribe the entire observer chain is executed (including get). You have to do something like this:

import { publishReplay, refCount } from 'rxjs/operators';    

return this.http.get<Article[]>(this.articlesUrl)
      .pipe(
       publishReplay(),
       refCount())

it will remit the value for every subscription.

Upvotes: 4

Related Questions