Search code examples
angularrxjsangular5ngrx

Dispatch backend call once in Angular app


I'm using Angular 5 with NgRX.

I have a component which defines a form to edit user settings. When the form loads, I want to set the current value of the settings in the input fields.

Currently I'm dispatching an event to retrieve those settings from the backend whenever the component is loaded.

class SettingsComponent implements OnInit {
  settings$ = this.store.select(fromSettings.getSettings);

  constructor(private store: Store<fromSettings.State>) {
  }

  ngOnInit() {
    this.store.dispatch(new SettingsActionTypes.RetrieveSettings());
  }
}

The effect which picks up this action:

@Effect()
findSettings$ = this.actions$
    .ofType(SettingsActionTypes.RetrieveSettings)
    .exhaustMap(() =>
        this.settingsService
            .getSettings() // http call
            .map((settings: Settings) => {
                return new SettingsFound({settings});
            })
            .catch(error => of(new SettingsNotFound()))
    );

This works pretty well, except that whenever a user navigates to my SettingsComponent, the backend is called. I want to use this state management in the frontend to its full extent, and as the state in the backend will only change through the frontend, I don't want to retrieve my settings multiple times.

I know I could dispatch the RetrieveSettings action in my Angular App component, but that means that the backend is called regardless that the user needs his/her settings retrieved.

I could also cache the backend call, but I expect this functionality from the state store...

So how could I execute my backend call when the SettingsComponent is loaded for the first time?


Solution

  • Use bygrace's answer made me think about applying the same principle in the initialization of the component.

    ngOnInit() {
        this.settings$
            .do(settings => {
                if (!settings) {
                    this.store.dispatch(new SettingsActionTypes.RetrieveSettings());
                }
            })
            .take(1)
            .subscribe()
    }
    

    Although I still feel there should be a more elegant solution to this problem.