0
votes

In my form I will have the following elements:

  1. input (checkbox)
  2. code (span)
  3. name (span)

These values ​​come from the server, which is why it is added dynamically.

However, I face the following problem with my algorithm:

core.js: 6228 ERROR Error: Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays.

Only this error does not make sense, because the console is showing the values ​​correctly and the guy is an Array:

enter image description here

MAIN COMPONENT

@Component({
    selector: 'zx-create-country-form',
    templateUrl: './create-country-form.component.html',
    styleUrls: ['./create-country-form.component.scss'],
})
export class CreateCountryFormComponent implements OnInit {
    createCountryForm!: FormGroup;
    dataTemp = [
        {
            code: 'BR',
            name: {
                pt_BR: 'Brasil',
            },
        },
        {
            code: 'TST',
            name: {
                pt_BR: 'tst3',
            },
        },
    ];

    constructor(protected readonly componentService: ComponentService, protected readonly formBuilder: FormBuilder) {
        this.createCountryForm = formBuilder.group({
            countries: formBuilder.array(this.addCountries()),
        });
    }

    ngOnInit(): void {}

    addCountries(): FormGroup[] {
        const formsGroups: FormGroup[] = [];

        this.dataTemp.map(country => {
            formsGroups.push(
                this.formBuilder.group({
                    radio: this.formBuilder.control([{ value: false, disabled: true }], [Validators.requiredTrue]),
                    code: this.formBuilder.control(country.code, [Validators.required, Validators.minLength(1), Validators.maxLength(2)]),
                    name: this.formBuilder.control(country.name.pt_BR),
                })
            );
        });

        return formsGroups;
    }
}

CHILD COMPONENT

<zx-form [fmGroup]="fmGroup">
    <div [formArrayName]="fmArrayName" *ngFor="let country of fmGroup.controls.countries; let i = index">
        <div [formGroupName]="i">
            <input formControlName="radio" placeholder="Item name" />
            <input formControlName="code" placeholder="Item name" />
            <input formControlName="name" placeholder="Item name" />
            eqqe
        </div>
    </div>
</zx-form>
1

1 Answers

0
votes

see you

countries: formBuilder.array(this.addCountries()),

this.addCountries must return an Array of FormGroups. If you use map, you get a new array in witch element is a transform of the original array. So

return this.dataTemp.map(country => 
                this.formBuilder.group({
                    radio: this.formBuilder.control([{ value: false, disabled: true }], [Validators.requiredTrue]),
                    code: this.formBuilder.control(country.code, [Validators.required, Validators.minLength(1), Validators.maxLength(2)]),
                    name: this.formBuilder.control(country.name.pt_BR),
                })
        );

Using a forEach, you loop over the array and make "something" with each element, as you are trying

    const formsGroups: FormGroup[] = [];

    this.dataTemp.forEach(country => {
        formsGroups.push(
            this.formBuilder.group({
                radio: this.formBuilder.control([{ value: false, disabled: true }], [Validators.requiredTrue]),
                code: this.formBuilder.control(country.code, [Validators.required, Validators.minLength(1), Validators.maxLength(2)]),
                name: this.formBuilder.control(country.name.pt_BR),
            })
        );
    });

Another point of attention is how iterate in the .html, you need iterate over the controls of the form Array, for this, create a getter of the array

get myArrayForm()
{
     return this.fmGroup.get('fmArrayName') as FormArray
}

And iterate over myArrayForm.controls, but get out the formArray name, and see that it's not enclosed by []

<div formArrayName="fmArrayName">
   <div *ngFor="let country of myArrayForm.controls; let i = index">
        <div [formGroupName]="i">
            <input formControlName="radio" placeholder="Item name" />
            <input formControlName="code" placeholder="Item name" />
            <input formControlName="name" placeholder="Item name" />
            eqqe
        </div>
    </div>
</div>

Really it's not good explain in the official docs, but you can see,e.g. this Netanel Basal's entry

Updated to validae if at least one of the elements of the array is true we need create a custom validation of the own array you can create the validator in the own component

  atLeastOne()
  {
    return (formArray:FormArray)=>{
      return formArray.value && formArray.value.filter(x=>x.radio).length<=0?
            {error:"At least one"}:null
    }
  }

And you use when you create the form

createCountryForm = this.formBuilder.group({
    countries: this.formBuilder.array(this.addCountries(), this.atLeastOne())
  });

NOTE: When we create a custom validator over a FormArray, we need take account that this is checked each time the formArray (or any of this FormGroups or Forms controls change)