I want to manage the state of my form using ngrx-forms library; so following docs: https://ngrx-forms.readthedocs.io/en/master/
i have a reducer:
import { Action, createReducer } from '@ngrx/store';
import {
FormGroupState,
createFormGroupState,
formGroupReducer,
onNgrxForms,
} from 'ngrx-forms';
import { Gender } from '../../core/enums/gender_enum';
// 1. Definicja modelu danych formularza
export interface PatientFormValue {
name: string;
last_name: string;
age: number;
gender: Gender;
}
// 2. Początkowy stan formularza
const FORM_ID = 'PatientFormValue';
const initialFormState = createFormGroupState<PatientFormValue>(FORM_ID, {
name: '',
last_name: '',
age: 0,
gender: Gender.nie_podawać,
});
// 3. Definicja stanu aplikacji
export interface PatientDataState {
someOtherField: string;
myForm: FormGroupState<PatientFormValue>;
}
const initialState: PatientDataState = {
someOtherField: '',
myForm: initialFormState,
};
// 4. Reducer zwiazany a formularzem
export function patientFormReducer(
state = initialState,
action: Action,
): PatientDataState {
const myForm = formGroupReducer(state.myForm, action);
if (myForm !== state.myForm) {
state = { ...state, myForm };
}
switch (action.type) {
case 'some action type':
return state;
default: {
return state;
}
}
}
export function reducer(state: PatientDataState | undefined, action: Action) {
return patientFormReducer(state, action);
}
then the app html:
<ng-container *ngIf="formState$ | async as formState">
<form novalidate [ngrxFormState]="formState">
<input
type="text"
[ngrxFormControlState]="formState.controls.someTextInput"
/>
<input
type="checkbox"
[ngrxFormControlState]="formState.controls.someCheckbox"
/>
<input
type="number"
[ngrxFormControlState]="formState.controls.nested.controls.someNumber"
/>
</form>
</ng-container>
and compontent.ts
import { Component } from '@angular/core';
import { OverviewNavComponent } from '../overview-nav/overview-nav.component';
import { RouterLink, Router, ActivatedRoute } from '@angular/router';
// store
import { Store, StoreModule } from '@ngrx/store';
import { FormGroupState, NgrxFormsModule, setValue } from 'ngrx-forms';
// icons
import { NgIconComponent, provideIcons } from '@ng-icons/core';
import {
bootstrapArrowRightCircleFill,
bootstrapArrowLeftCircleFill,
} from '@ng-icons/bootstrap-icons';
import { Observable } from 'rxjs';
import {
PatientDataState,
PatientFormValue,
} from '../../../../store/patient-store/patients.reducer';
@Component({
selector: 'app-patients-data',
standalone: true,
imports: [
NgIconComponent,
OverviewNavComponent,
RouterLink,
NgrxFormsModule,
StoreModule,
],
templateUrl: './patients-data.component.html',
styleUrl: './patients-data.component.css',
viewProviders: [
provideIcons({
bootstrapArrowRightCircleFill,
bootstrapArrowLeftCircleFill,
}),
],
})
export class PatientsDataComponent {
formState$: Observable<FormGroupState<PatientFormValue>>;
constructor(
private router: Router,
private activatedRoute: ActivatedRoute,
private store: Store<PatientDataState>,
) {
this.formState$ = store.select((s) => s.myForm);
}
// updateFormValue(newValue: PatientFormValue) {
// this.store.dispatch(setValue({ value: newValue }));
// }
}
For now, when i access the component i get error console:
The pipe 'async' could not be found
so i add import:
CommonModule
and then i get:
Property 'someTextInput' does not exist on type 'FormGroupControls<PatientFormValue>'.
Another problem is using
<ng-container *ngIf="formState$ | async as formState">
as I am using Angular17 and i am not sure if cant switch to:
@if(formState$ | async as formState){ }
When i import the CommonModule i get multiple errors but i fix the pipe problem. Without CommonModule i get pipe problem but the form seems to work. How to save and retrive data to ngrx Store using ngrx forms and Angular 17? How to fix async pipe problem?
I tried adding and removing CommonModule and rewriting the @if statement. Also, i dont really understand how to manipulate data- change the data on change() using this library.
edit: after adding CommonModule i have now:
inject() must be called from an injection context such as a constructor, a factory function, a field initializer
Async pipe is something that needs to be imported to be used.
When working with standalone components you can see standalone: true
set on the component decorator.
You can either import AsyncPipe
or CommonModule
(contains many directives and pipes, e.g. NgIf
, CurrencyPipe
, NgFor
) from '@angular/common'
.
...
import { CommonModule } from '@angular/common';
...
...
@Component({
selector: 'app-patients-data',
standalone: true,
imports: [
CommonModule, // <- changed here!
NgIconComponent,
OverviewNavComponent,
RouterLink,
NgrxFormsModule,
StoreModule,
],
...