MrWolfZ / ngrx-forms

Enhance your forms in Angular applications with the power of ngrx
MIT License
374 stars 112 forks source link

Use the FormControlD directly to update a (nested) state? #262

Open schnebdreleg opened 2 years ago

schnebdreleg commented 2 years ago

This is more of a question than a feature or bug report.

Currently I have something like this within my reducer:

const enableFormControl = (state: FormState, {controlId}: { controlId: string }) => {
    const attributeKey = findAttributeControlKeyByControlId(state, controlId);

    return updateGroup<FormValue>({
        attributes: (attributeControls) => {
            const updateFns = [] as StateUpdateFns<FormValue['attributes'][];
            updateFns.push({[attributeKey]: enable}); 
            // more modifiers ...
            return updateGroup(attributeControls, updateFns);
        },
    })(state);
};

I use this pattern often within the reducer to apply multiple updates at the same time to a form control (enable, setValue, markAsDirty...). I therefore do not use the actions provided by ngrx-forms, but my own. However, as soon as the form state contains several nested structures, the dynamic determination of the correct "key" in the respective nested control (here, the attributeKey in the nested 'attributes') becomes quite complex.

Is there a way to apply updates with e.g. 'updateGroup' directly by using just the FormControlID provided by an action that I did not see, yet? Something like updateGroup<FormValue>(state, formControlID, updateFns[]) ?

dzonatan commented 2 years ago

Not out-of-the-box but you can achieve this by using the updateRecursive function. Although, the main challenge here is gonna be handling the type-safety. Pseudo-code:

const updateByControlId = (state, formControlId, updateFns[]) => 
    updateRecursive(
      state, 
      s => s.id === formControlId ? 
          updateFns.reduce((finalState, updateFn) => updateFn(finalState), s) : s
    );
MrWolfZ commented 2 years ago

Indeed, updateRecursive is the function that you are looking for. If you can't come up with proper types for this function, please post here again and we can do it together.

schnebdreleg commented 2 years ago

Thank you for the quick responses. I came across another way last night and I now wonder if that would be equivalent? Did not know it is possible to use the Actions of ngrx-forms within the reducer:

  let form = state;

  form = formStateReducer(form, new ResetAction(controlId));
  form = formStateReducer(form, new DisableAction(controlId));
  ...
    form = updateGroup<FormValue>({
        attributes: (attributeControls) => {
            const updateFns = [] as StateUpdateFns<FormValue['attributes'][];
            updateFns.push({[attributeKey]: reset});
            updateFns.push({[attributeKey]: disable});
            return updateGroup(attributeControls, updateFns);
        },
    }, )(state);

I've tried the formStateReducer approach in combination with the controlId of a nested form control on the root node of the form state and it seems to be doing the same thing?