Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ControlValueAccessor with FormArray in Angular 2

Tags:

forms

angular

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

like image 758
Anonymous Me Avatar asked Nov 05 '16 14:11

Anonymous Me


People also ask

What is Controlvalueaccessor in Angular?

Control Value Accessor is an interface that provides us the power to leverage the Angular forms API and create a communication between Angular Form API and the DOM element. It provides us many facilities in angular like we can create custom controls or custom component with the help of control value accessor interface.

What is the use of FormArray in Angular?

A FormArray aggregates the values of each child FormControl into an array. It calculates its status by reducing the status values of its children. For example, if one of the controls in a FormArray is invalid, the entire array becomes invalid.

How do I add validation to FormArray?

Validating Angular FormArray First you need to add the required validators while creating a new product form group inside the addProduct method. Now let's add a span element adjacent to the input control. Add the following CSS to the app. component.


2 Answers

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" };
    }
}
like image 68
Eliseo Avatar answered Sep 27 '22 23:09

Eliseo


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!

like image 28
Serginho Avatar answered Sep 27 '22 23:09

Serginho