23

I have a child component which deals with the array of input controls. I want to have a formcontrol over the child component.

I am passing the array of json object, what would be the correct way to bind parent form to the child component's FormArray having 2 form control with Validator required on first.

This is the initial code

<h1>Child</h1>
<div formArrayName="names">
 <div *ngFor="let c of names.control">
  <input formControlName="firstName">
  <input formControlName="lastName">
 </div>
</div>

Intention is to bind parent form with the array of input control in the child component. Also form will become invalid if one of the input control in child component doesn't have required field.

http://plnkr.co/edit/HznCJfSEiSV28ERqNiWr?p=preview

4
  • Could you clarify what your intent is with setting the username form control to a json object? Are you attempting to list, in the child component, a first name and last name for each firstname lastname pair in the json? Commented Nov 5, 2016 at 20:16
  • Yes, with the given json, I am trying to render each pair in the input control. Commented Nov 5, 2016 at 22:36
  • 1
    @AnonymousMe, have you find any solution on this problem ? Commented Aug 2, 2019 at 6:59
  • It is easy to do so with ngx-sub-form see github.com/cloudnc/ngx-sub-form#dealing-with-arrays Commented Aug 21, 2019 at 12:17

2 Answers 2

11

I love solve old post :)

The key is that your custom Form Component has inside a FormArray, then use "writeValue" to create the formArray, see stackblitz

@Component({
    selector: "my-child",
    template: `
    <h1>Child</h1>

    <div *ngFor="let group of formArray.controls" [formGroup]="group">
        <input formControlName="firstName" (blur)="_onTouched()" />
        <input formControlName="lastName" (blur)="_onTouched()"/>
    </div>
    `,
    providers: [{
            provide: NG_VALUE_ACCESSOR,
            useExisting: Child,
            multi: true
        },
        {
            provide: NG_VALIDATORS,
            useExisting: Child,
            multi: true
        }
    ]
})
export class Child implements ControlValueAccessor {
    formArray: FormArray;
    _onChange;
    _onTouched;

    writeValue(value: any) {
        this.formArray = new FormArray(
            value.map(x => {
                return new FormGroup({
                    firstName: new FormControl(x.firstName, Validators.required),
                    lastName: new FormControl(x.firstName, Validators.required)
                });
            })
        );
        this.formArray.valueChanges.subscribe(res => {
            this._onChange(res);
        });
    }

    registerOnChange(fn: (value: any) => void) {
        this._onChange = fn;
    }

    registerOnTouched(fn: (value: any) => void) {
        this._onTouched = fn;
    }

    validate({ value }: FormControl) {
        return !this.formArray || this.formArray.valid ?
            null : { error: "Some fields are not fullfilled" };
    }
}
Sign up to request clarification or add additional context in comments.

Comments

1

You have to use formArrayName directive and *ngFor like this:

<form [formGroup]="form" (ngSubmit)="sayHello()">
       <input formControlName="name"><br>
       <input formControlName="email"><br>
       <div formArrayName="username">
           <div *ngFor="let user of username.controls; let i=index">
                <my-child formControlName="i"></my-child>
           </div>
       </div>
       <button type="submit">Register</button>
</form>

And with FormBuilder you have to use FormArray as well.

form = new FormGroup({
     name: new FormControl('My Name'),
     username: new FormArray([
                  new FormControl("value"),// ControlValueAccesor is applied only to one control, not two. So you cannot use javascript object like you are using below this line.
                  {firstName:"Anna", lastName:"Smith"},
                  {firstName:"Peter", lastName:"Jones"}
                ])
   });

For more details, see this doc.

Case 2: passing FormGroup:

form = new FormGroup({
     name: new FormControl('My Name'),
     username: new FormArray([
                  new FormGroup({
                        firstName: new FormControl('Anna'),
                        lastName: new FormControl('Smith')
                  }),
                  new FormGroup({
                        firstName: new FormControl('Peper'),
                        lastName: new FormControl('Jones')
                  }),
                ])
   })

If you are tring to pass the FormGroup as a ngModel parameters, you can't!

4 Comments

This is fine when formArray is created in parent component itself but I would like to have a child component with a formArray.
You are thinking in a wrong and anti-pattern approach. The question is: Why do you want to embed a FormGroup into a new component? That's not a good approach. But, if you insists on do it, you can create a component that gets a FormGroup and use it inside.
How can that be an anti pattern, child component would have to reside in a library and will be consumed by many pages. Rather copy pasting the code everywhere better make a child component and consume it as is in many places.
You can only pass one parameter to ngModel, you cannot transform 2 params in one. This component will never works with ngModel, neither your apps nor itself.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.