[this question is based on https://ngrx-forms.readthedocs.io/]
I've an array of topics(strings) inside my angular component. I'm using ngrx-store to manage state along with ngrx-forms for managing forms. During the initialization of the component, I dispatch some actions for each topic inside the component.
ngOnInit(): void {
this.formState$ = this.store.pipe(select(s => s.filterByTopics.formState))
this.topicsOptions$ = this.store.pipe(select(s => s.filterByTopics.topicsOptions))
Object.keys(this.topics).forEach(topic => this.store.dispatch(new CreateTopicControlAction(topic)))
}
That works fine and the ngrx-form controls are getting added.
But the real issue is that if I again visit the same component again it reinitializes the actions (since ngOnInit contains all the actions) and spits out an error:
Uncaught Error: Group 'filterByTopicsForm.topics' already has child control '0'!
How can I prevent this?
Is there any other workaround?
Author of ngrx-forms here.
This is not an issue with ngrx-forms per se, but more a general question of how to prevent double initialization when state is in the store, and therefore not coupled to a component's lifetime.
I see multiple options here:
reset the form in ngOnDestroy
by dispatching a SetValueAction
with an initial value as well as a ResetAction
(or create your own custom action to recreate the form state in the reducer); this emulates the behaviour of @angular/forms where a form lives only as long as the owning component; here's how that could look like with a custom action with ngrx v8+:
const resetMyForm = createAction('MY_FORM/reset');
const myFormReducer = createReducer(
{
formState: createFormGroupState('MY_FORM', INITIAL_FORM_VALUE),
},
onNgrxForms(),
on(resetMyForm, ({ formState }, { lang, code }) => ({
formState: createFormGroupState('MY_FORM', INITIAL_FORM_VALUE),
})),
);
Check if the form is already initialized in the component
ngOnInit(): void {
this.formState$ = this.store.pipe(select(s => s.filterByTopics.formState))
this.topicsOptions$ = this.store.pipe(select(s => s.filterByTopics.topicsOptions))
Object.keys(this.topics).forEach(topic => {
this.formState$.pipe(take(1)).subscribe(formState => {
// how exactly this `if` looks like depends on your concrete form state shape
if (!formState.controls.topics.controls[topic]) {
this.store.dispatch(new CreateTopicControlAction(topic))
}
})
})
}
Check if the form is already initialized in the reducer
const createTopicControl = createAction('MY_FORM/createTopicControl', (topic: string) => ({ topic }));
const myFormReducer = createReducer(
{
formState: createFormGroupState('MY_FORM', INITIAL_FORM_VALUE),
},
onNgrxForms(),
on(createTopicControl, (state, { topic }) => {
if (state.formState.controls.topics.controls[topic]) {
return state
}
// add form control ...
}),
);
I hope this helps.