Skip to content

Add a form management feature to a signal store #45

Open
@ebondu

Description

@ebondu

When using complex forms in Angular, it could be convenient to handle the form management with a dedicated store feature.

Some commons scenarios where adding a withForm() feature to a signal store could be useful:

  • cross-controls management: updating a form control on a change on another control. Using a store computed method seems relevant to handle the form update business logic.
  • form processing management: after computed changes has been applied to the form, calling a store method to process the form (i.e. calling a service).
  • form controls value as a signal: accessing to a store state representing the form controls values as a signal. Could be used to create new computed values or when processing the form.
  • Combined with the withSync() feature to the form state could be saved in the localStorage

To connect the form to the store, a dedicated form directive should be used.

The approach could look like this:

In template, using the appSignalStoreForm directive :

<div [formGroup]="myForm" appSignalStoreForm [store]="myStore" formFeatureName="mySearch">
            <select formControlName="period">...</select>
            <input formControlName="from">
            <input formControlName="to">
...
</div>

In the component, nothing required except the store:

export class SearchComponent {
    readonly myStore = inject(MyStore);
    ...
}

In the store, declaring the withForm feature and the required computed and method :

export const MyStore = signalStore(
    withForm('mySearch'),
    withComputed(({ mySearchFormState }) => {
        return {
            // method called by the form directive to compute changes on form changes
            mySearchFormComputedChanges: computed<ControlChange[]>(() => {
                const changes: ControlChange[] = [];

                // here is the business logic to update the form as a side effect 
                if (mySearchFormState().controlsMarkedAsChanged.some((control) => control === 'period')) {
                    if (mySearchFormState().value.period === 'current') {
                        changes.push({ controlName: 'from', operation: new SetValue(now()) });
                        ...
                    }
                }
                // the list of changes the form directive will apply on the form
                return changes;
            }),
        };
    }),
    withMethods((store) => ({
        // method called by the directive after changes applied
        handleMySearchForm() {
           // using the form values representation to do something
           search(store.mySearchFormState().value?.from, store.mySearchFormState().value?.to);
        }
    }))

Let me know if you think this is feature could be relevant for the project.

Metadata

Metadata

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions