als9xd
als9xd

Reputation: 854

Correct way to have persistent data in service

I'm trying to fetch items from a database only once (I don't expect them to change). When I initially load home-page.component.ts the HomePageComponent.items gets correctly set however if I navigation away from HomePageComponent to ProfilePageComponent and then back to HomePageComponent because the itemsService.items$ has already been resolved the subscribe callback isn't rerun and HomePageComponent.items isn't reinitialized. This is the current workaround I'm using but it feels too clunky to be the correct way of handling this situation.

home-page.component.ts

ngOnInit() {
    this.itemsService.items$.subscribe((items:item) => {
      // Is only called once after items are initalially fetched over http
      this.items = items;
      this.itemsService.addItems(items);
    });

    if(this.itemsService.foundItems){
      this.items= this.itemsService.items;
    }
}

items.service.ts

export class ItemsService {

  private items = new BehaviorSubject(null);
  items$ = this.items.asObservable();

  foundItems = null;

  constructor(private httpClient: HttpClient){
    this.items.next(httpClient.post<any>(myUrl,myQuery).subscribe(this.items));
  }

  addItems(items){
   this.foundItems = items;
  }
}

Upvotes: 3

Views: 444

Answers (2)

Igor
Igor

Reputation: 62213

The component should not be aware of any caching, you need to abstract that. The service can cache the retrieved items and then return the items to any subsequent calls wrapped in an observable using of. The service itself can cache the result of the call using tap.

home-page.component.ts

ngOnInit() {
    this.itemsService.getItems().subscribe((items) => {
      this.items = items;
    });
}

items.service.ts

import { map, tap } from 'rxjs/operators';
import { of } from 'rxjs';

export interface IItem {
}

export class ItemsService {

  items: IItem[];

  constructor(private httpClient: HttpClient){
  }

  getItems() : Observable<IItem[]> {
    return this.items 
        ? of(this.items)
        : this.httpClient.get<IItem[]>(myUrl).pipe(tap((data) => this.items = data));
  }
}

For the purposes of the example I added an interface named IItem so I could strongly type the calls and signatures. You can replace that with whatever interface type you needed.

Upvotes: 6

Nirnay Kulshreshtha
Nirnay Kulshreshtha

Reputation: 73

unsubscribe your observer on destroy of home-page component.

itemOb: any;

take this as global

ngOnInit() {
    this.itemOb = this.itemsService.items$.subscribe((items:item) => {
      // Is only called once after items are initalially fetched over http
      this.items = items;
      this.itemsService.addItems(items);
    });

    if(this.itemsService.foundItems){
      this.items= this.itemsService.items;
    }
}
    ngOnDestroy() {
if (this.itemOb) {
    this.itemOb.unsubscribe(); }
    }

Try this your problem can be resolved

Upvotes: 0

Related Questions