Where to provide angular services for storing data

98 Views Asked by At

I want to use ngrx component-store/service with a subject in my application, however I am in a situation where I am not sure whats best. I hope you can guide me in the right direction. (The following example is a simplified version of my real scenario).

  • I have a MoviesComponent with a MoviesStore, containing all movies.
  • Route /movies-list directs to the MoviesListComponent. The MoviesListStore holds a string which is used for searching in the list
  • Route /movies-detail directs to MoviesDetailComponent and the MoviesDetailStore saves the expaned sections in the page.

enter image description here

This works pretty well.

BUT: When I navigate back and forth between /movies-list and /movies-detail I want that both, searchFor and expandedSections will preserve its values. With the current architecture the values are discarded since the MoviesListStore and MoviesDetailStore are destroyed as I navigate between them.

Approach #1

I could just move the MoviesListStore and MoviesDetailStore the the provider of MoviesComponent. enter image description here

  • every component still has its own dedicated store
  • might be confusing since the store is not provided on the component's level?!

Approach #2

I could get rid of the MoviesListStore and MoviesDetailStore and put the data in into the MoviesStore like this enter image description here

  • The MoviesStore now holds every data, no clear separation. The store might become super big.
2

There are 2 best solutions below

0
rasiaq On BEST ANSWER

I think the Approach #2 is the one you should go with. I agree that when you have a data related only to a specific module or a components subtree there is no need to put it in global store, because of two reasons:

  1. It will be available everywhere in your application (using selector)
  2. ngrx global store is never destroyed, because it is provided in root. So once you create an instance of it, or you register a feature slice it will always be there, along with the data (you can clear the data manually, but reducer, effects, actions etc. of your feature stays there).

The Approach #2 is actually called component "branch" state:

This is the one when you define component store in one node and that state would be used down in the component tree as well

This quote comes from How to use NgRx ComponentStore? - Alex Okrushko | NG-DE 2022. Using this approach, you will have one instance of component store for a specific component subtree. During the navigation between movies-list and movies-detail components, the data will be preserved, but once the "highest" node is destroyed (in your example its movies.component) the component store will also be destroyed along with the stored data. When the instance of movies.component is created, an instance of a movies.store.ts will be created as well.
I also recommend checking component-store vs global store comparison in official NGRX docs. You might find some answers there as well

3
Volodymyr Usarskyy On

IMHO, using just a components store in your case is wrong choice because you are limited by a component scope. You kind of realized it because you are here :)

The obvious solution is to introduce a global (i.e., module-level) state to persist the data. Basically, all of your solutions are actually a way of creating this global store.

From your description I can see that you decided not to use NgRX store but to go ahead with an Observable Data Service pattern (that's what you call service with a subject). While this is absolutely valid approach, it has its limitations and now you are experiencing one of them.

I don't know why you don't want to use any of the existing global store implementations but I would strongly suggest to take a look on this option as well. It is definitely more complicated but will be more understandable at the end.

"Approach #1" is terrible because you separate component store that belong to a particular component and should be visible just from this component from the component itself. I personally wouldn't do it.

"Approach #2" is actually a global state :) you just don't call it so. But it has one great disadvantage: data that belongs to MoviesListComponent will be stored in MoviesComponent.

There are two more ways to solve your problem:

  • Approach #3: add filters state into page query params during the redirect. The url will look like http://your.website/movie-details/blahblahblah123?usedFilters=blah. Redirecting back to movies list will restore the filter state.
  • Approach #4: involves some UI changes: you can use named router outlets. Basically, your URLs will look like http://your.website/movies-list(sidebar:movie-details/blahblahblah123)?serachFor=blah. This means that both component instances (MoviesListComponent and MovieDetailsComponent) are alive simultaneously and MoviesListComponent is not destroyed between navigations.