Skip to content

Implementation error while dealing with set value for a FormArray of FormGroups #366

Open
@shannon-rodricks

Description

@shannon-rodricks

Hi,
Firstly, thanks for this library. Its amazing for dealing with forms and saves a lot of time!

I am currently using it to create/edit events, with the following fields

  • Name
  • Description
  • Date
  • Start Time
  • Pricing [] (list of formgroups)
    • Category (text field with typeahead)
    • Price (text field with int type)

Defined as so:

var form = FormGroup({
  "name": FormControl<String>(value: "", validators: [Validators.required]),
  "description": FormControl<String>(value: ""),
  "date": FormControl<DateTime>(value: null, validators: [Validators.required]),
  "time": FormControl<DateTime>(value: null, validators: [Validators.required]),
  "pricing": FormArray([])
});

var pricingGroup = FormGroup({
  "category": FormControl<String>(value: "", validators: [Validators.required]),
  "cost": FormControl<int>(value: 0, validators: [Validators.required])
});

The issue arises when I attempt to set a value to the form, to edit an existing event.

form.value = {
  "name": "Event Name",
  "description": "Fake description",
  "date": DateTime.now(),
  "time": DateTime.now(),
  "pricing": [
    pricingGroup..value = {
      "category": "General",
      "cost": 200
    }
  ]
};

What I'm doing is converting the json to a formgroup, for each pricing object, which i need to do to ensure that the validators are applied, or for more advanced requirements (formgroups with nested form arrays etc).
The issue arises in this code in FormArray

@override
  void updateValue(
    List<T?>? value, {
    bool updateParent = true,
    bool emitEvent = true,
  }) {
    for (var i = 0; i < _controls.length; i++) {
      if (value == null || i < value.length) {
        _controls[i].updateValue(
          value?.elementAt(i),
          updateParent: false,
          emitEvent: emitEvent,
        );
      }
    }

    if (value != null && value.length > _controls.length) {
      final newControls = value
          .toList()
          .asMap()
          .entries
          .where((entry) => entry.key >= _controls.length)
          .map((entry) => FormControl<T>(value: entry.value))
          .toList();

      addAll(
        newControls,
        updateParent: updateParent,
        emitEvent: emitEvent,
      );
    } else {
      updateValueAndValidity(
        updateParent: updateParent,
        emitEvent: emitEvent,
      );
    }
  }

specifically

          .map((entry) => FormControl<T>(value: entry.value))

This wraps every formgroup in a formcontrol, which breaks form handling (validations and .value don't work correctly)

To resolve my issue, I've done this

final List<AbstractControl<T>> newControls = value
          .toList()
          .asMap()
          .entries
          .where((entry) => entry.key >= _controls.length)
          .map((entry) {
        if (entry.value is AbstractControl) {
          return entry.value as AbstractControl<T>;
        }
        return FormControl<T>(value: entry.value);
      })
          .toList();

which makes everything work correctly.

Does it make sense to change the code to this?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions