Search code examples
angularngrxngrx-store

How to reload data when "hard navigate" with ngrx/store?


In my application I retrieve all data in the "Items-Overview-Component" in the constructor. (router path: "/items").

// Items-Overview-Component

constructor(private store: Store<fromReducer.State>) {
    this.store.dispatch(loadItems());
}

When clicking on an item entry, I navigate to "/items/:id". Then I retrieve the details from the store using the :id.

// Item-Details-Component

constructor(private store: Store<fromReducer.State>) {
    this.item$ = this.store.pipe(select(getItemDetails));
}

So far a very clear vanilla use case for an Angular project.

However, if the user navigates "hard" to a URL with a specific item (e.g. "/items/741"), then the store is empty and no details can be retrieved.

How can I reload the data in NGRX "on demand"? Is there perhaps a one-time lifecycle hook that I should react to here?

BTW: I also use @ngrx/router-store


Solution

  • The pattern you want is most likely to use a guard on the /items route, instead of relying on the Items-Overview-Component to trigger loading items.

    It could look like:

    @Injectable({
      providedIn: 'root',
    })
    export class ItemsGuard implements CanActivate {
      constructor(
        private store: Store<AppState>
      ) {}
    
      public canActivate(): Observable<boolean> {
        this.store.dispatch(loadItems()); // immediately trigger loading items
    
        return this.store.select(getItems).pipe(
          filter(items => !!items && items.length) // wait until the selector emits some non-empty items list
          mapTo(true) // as soon as the items are present, allow the navigation event to complete
        );
      }
    }
    

    Then you can use it in your routes file like this:

        path: 'items',
        canActivate: [ItemsGuard],
        ....
        children: [ 
          // '/:id' route goes here
        ]
    
    

    Since items/:id is configured as a child route of /items, ItemsGuard will trigger in both cases, even if after page refresh you directly enter /items/123 without ever entering /items.