Fetch data once with Observables in Angular 2

I have a service that is used several times from a large number of my Angular 2 components. It retrieves client data from the web API and returns Observable:

getCustomers() {
   return this.http
        .get(this.baseURI + this.url)
        .map((r: Response) => {
            let a = r.json() as Customer[];                       
            return a;                         
        });               
}    

I am embedding this service in my root component, and in every component that wants to access clients, I just subscribed to this Observable:

this.customerService.getCustomers().subscribe(v => this.items = v);

However, every component that subscribes to my Observable causes a different HTTP request execution. But to receive data, only once is enough. If I try share (), this will not solve my problem:

getCustomers() {
   return this.http
        .get(this.baseURI + this.url)
        .map((r: Response) => {
            let a = r.json() as Customer[];                       
            return a;                         
        }).share();               
}   

Still the same problem. Any suggestions which operators should I use to retrieve data only once?

+7
4

1) :

export class CustomersService {
  protected _customers: Array<Customer>;

  constructor(public http: Http) {}

  public getCustomers(): Observable<Array<Customer>> {
    return new Observable(observer => {
      if (this._customers) {
        observer.next(this._customers);
        return observer.complete();
      }
      this.http
        .get(this.baseURI + this.url)
        .map((r: Response) => (r.json() as Array<Customer>))
        .subscribe((customers: Array<Customer>) => {
          this._customers = customers;
          observer.next(this.customers);
          observer.complete();
        });
    });
  }
}

2) refresh:

export class CustomersService {
  protected _customers: Array<Customer>;

  constructor(public http: Http) {}

  public getCustomers(refresh?: boolean): Observable<Array<Customer>> {
    if (!refresh && this._customers) {
      return Observable.of(this._customers);
    }
    return this.http
            .get(this.baseURI + this.url)
            .map((c: Response) => (c.json() as Array<Customer>))
            .do((customers: Array<Customer>) => {
                this._customers = customers;
            });
    });
  }
}

3) ReplaySubject:

export class CustomersService {
  protected _customers$: ReplaySubject<Array<Customer>> = new ReplaySubject(1);
  protected _customersInitialized: boolean;

  constructor(public http: Http) {}

  public getCustomers(refresh?: boolean): Observable<Array<Customer>> {
    if (refresh || !this._customersInitialized) {
      this._customersInitialized = true;
      this.http
        .get(this.baseURI + this.url)
        .map((c: Response) => (c.json() as Array<Customer>))
        .subscribe((customers: Array<Customer>) => {
          this._customers$.next(customers);
        });
    }
    return this._customers$.asObservable().skip(+refresh).distinctUntilChanged();
  }
}

:

this.customersService.getCustomers()
    .subscribe(customers => this.customers = customers);

customers SomeService (, ) :

public get customers(): ReadonlyArray<Customer> {
  return this._customers;
}
+10

, , , .

, , (Observable.fromPromis(stream.toPromise()), . View https://www.bennadel.com/blog/3184-creating-leaky-abstractions-with-rxjs-in-angular-2-1-1.htm .

  //declare observable to listen to
  private dataObservable: Observable<any>;

  getData(slug: string): Observable<any> {

    //If observable does not exist/is not running create a new one
    if (!this.dataObservable) {

        let stream = this.http.get(slug + "/api/Endpoint")
            .map(this.extractData)
            .finally(() => {
                //Clear the observable now that it has been listened to
                this.staffDataObservable = null;
            });

        //Executes the http request immediately
        this.dataObservable = Observable.fromPromise(stream.toPromise());

    }        

    return this.staffDataObservable;
 }
+2

. , , getCustomers(), , share(), .

, HTTP-, , http , . .

-

@Injectable()
class FooBar {

    public dataStream:Subject<any> = new Subject();

    constructor(private http:Http) {}

    public getCustomers() {
        return this.http
        .get(this.baseURI + this.url)
        .map((response:Response) => response.json())
        .map((data) => {
            this.dataStream.next(data); 
            return data;
        })
    }

}


@Component({})
class BarFooHttpCaller {
    constructor(private foobar:Foobar) {}

    ngOnInit() {
        this.foobar.getCustomers().subscribe(() => { console.log('http done') });
        this.foobar.dataStream.subscribe((data) => {
            console.log('new data', data);
        })
    }
}

@Component({})
class OtherBarFoo {
    constructor(private foobar:Foobar) {}

    ngOnInit() {
        this.foobar.dataStream.subscribe((data) => {
            console.log('new data', data);
        })
    }
}
+1
source

I would create a parent container, retrieve the data once and pass it to the child components using @Input.

Parent:

@Component({
    selector: 'BarFooHttpCaller',
    template: ´<child *ngIf="data.length > 0" [data]></child>´
})

class BarFooHttpCaller {
    private data: any;
    constructor(private foobar:Foobar) {
        this.data = {};
    }

    ngOnInit() {
        this.foobar.getCustomers().subscribe(() => {       
            console.log('httpdone') 
        });
        this.foobar.dataStream.subscribe((data) => {
            console.log('new data', data);
            this.data = data;
        })
    }
}

Child:

import { Component, Input } from '@angular/core';

@Component({
    selector: 'child',
    template: ´<div>{{data}}</div>´
})

export class Child {
    @Input() data: any;

}
+1
source

Source: https://habr.com/ru/post/1660406/


All Articles