1
votes

I'm trying to understand what's the best way to store (and update) data from the api and share that data between siblings components. This is what I tried.

Saving the observable

export class MyExampleService {
   private data: Observable<any>;

   constructor(private readonly http: HttpClient) { }

   getData(): Observable<string[]> {
       //if we already got the data, just return that
       if (data) {
           return data;
       }

       //if not, get the data
       return this.http.get<string[]>('http://my-api.com/get-stuff')
           .pipe(tap((returnedData: string[]) => {
               //save the returned data so we can re-use it later without making more HTTP calls
               this.data= returnedData;
           }));
   }
}

But this approach doesn't really fit my needs because it doesn't use any subject and I want to tell my other components when data changes.

Using subjects

export class MyExampleService {
   private dataSbj: BehaviorSubject<any> = new BehaviorSubject(null);
   
   readonly data$ = this.dataSbj.asObservable();

   constructor(private readonly http: HttpClient) { }

   getData(): Observable<string[]> {

       if (dataSbj.getValue() === null) {
           return this.http.get<string[]>('http://my-api.com/get-stuff')
           .pipe(tap((returnedData: string[]) => {
               //save the returned data so we can re-use it later without making more HTTP calls
               this.dataSbj.next(returnedData);
           }));
       }          
   }
}

Then I would simply subscribe to the getData only the first time and subscribe to the data$ observable in all other components. (In this case I have one parent component and more child routes, so I would subscribe to getData() in the parent component and subscribe to the data$ in all child routes).

This last approach works but I'd rather use the same function to retrieve the same data and not by subscribing to different observables.

Is this considered a good approach or is there something better I could do instead?

2
store data in cookies, use authentication and authorization in you app to restrict data - surjendu_dey
this doesn't sound right. I mean you could do it, but I don't think it's the best way. I would much rather prefer in memory storing in my case - Marco Ripamonti

2 Answers

0
votes

If your getData() method has no parameters, you should be able to do the following by utilizing the shareReplay() operator. It will only call the API when the first consumers subscribes to it, and will return the last emit to any sequential calls. This also includes a way to update the data:

@Injectable({
  providedIn: 'root'
})
export class ExampleService {
  private updateData$ = new BehaviorSubject<void>(void 0); 

  readonly data$ = this.updateData$.pipe(
    switchMap(() => this.http.get('https://reqres.in/api/users')),
    shareReplay(1)
  );

  constructor(private readonly http: HttpClient) {}
   
  refresh(): void {
    this.updateData$.next();
  }
}
export class ExampleComponent {
  data$: Observable<any>;

  constructor(private example: ExampleService) {}

  getApi(): void {
    this.data$ = this.example.data$;
  }

  refresh(): void {
    this.example.refresh();
  }
}

working example Check the console log

1
votes

NGRX store state management is the approach to read from store if data is available instead of making unnecessary network call.

It takes care of create, update and delete records from store by dispatching relevant action.

Here is the complete source code DEMO on github of how ngrx store is being used.

I would strongly recommend to use ngrx store it has some cool features to avoid unnecessary network calls which caused the speed up of your app.