Search code examples
angularngrxangular-ngrx-datangrx-data

Angular & NgRx-Data update & subscription issue


I'm using Angular 11 & NgRx-Data 10.1.2. I am using the smart component/dumb component pattern.

In my 'list' view when I select an item I route to a detail page passing the item id.

In my detail page, I subscribe to the entity service getByKey()

this.siteEntityService.getByKey(this.siteId).subscribe((site: Site) => {
  this.site = site;
});

This is the SiteEntityService:

import { Injectable } from '@angular/core';
import { EntityCollectionServiceBase, EntityCollectionServiceElementsFactory } from '@ngrx/data';
import { Site } from 'src/app/models/site.model';

@Injectable({ providedIn: 'root' })
export class SiteEntityService extends EntityCollectionServiceBase<Site> {
  constructor(serviceElementsFactory: EntityCollectionServiceElementsFactory) {
    super('Site', serviceElementsFactory);
  }
}

When 'edit' is clicked, I open the edit component in a modal:

openEditModal(){
  this.showingModal = true;

  this.modalRef = this.modalService.open(SiteEditShellComponent);
  this.modalRef.componentInstance.site = this.site;
}

In the Edit component, when the form is submitted is clicked:

  formSubmitted(form: any) {
    let updatedSite = { ...this.site };
    updatedSite.name = form.siteName;

    this.siteEntityService.update(updatedSite).subscribe((x: Site) => {
      this.site = x;
      this.activeModal.dismiss();
    })
  }

After the update, if I investigate the store I can see that the entity has been updated correctly, however, after the modal has closed, the subscription on the display page doesn't update, despite the store (and the database) updating.

So where am I going wrong? How do I get the display page to refresh the data?

Edited in response to @Fiber's answer:

It's now working, however since the subscription is returning an array of the items, it means that I'm having to search the array for the correct one, which doesn't seem ideal.

  loadSite() {
    let sites$ = this.siteEntityService.store.select((new EntitySelectorsFactory().create<Site>('Site')).selectEntities);

    this.subscriptions.add(
      sites$.pipe(
        map((sites: Site[]) => {
          return sites.find((site: Site) => site.id === this.siteId);
        })
      ).subscribe((site: Site) => {
        this.site = site;
      })
    );

    this.siteEntityService.getByKey(this.siteId).subscribe((site: Site) => {
      this.site = site;
    });
  }

Solution

  • Will fire only once when it retrieves the data from the backend:

    this.siteEntityService.getByKey(this.siteId).subscribe((site: Site) => {
      this.site = site;
    });
    

    That method is connected to the data-retrieve-method, not the entity-store you are seeking.

    You should make an observable that connects directly to the store (ngrx/data provides you an EntitySelectorsFactory to be able to do that) and listen to the changes.

    // this is gonna fire every time you update the store with new 'Site' items
    sites$ = this.store.select((new EntitySelectorsFactory().create<Site>('Site')).selectEntities);
    
    sites$.subscribe((sites: Site[]) => {
      this.site = sites.find(s => s.id === this.siteId);
    });
    
    // this will trigger the initial load
    this.siteEntityService.getByKey(this.siteId);
    

    Obviously the subscriptions should be terminated at component destruction, but skipped that part for brevity.