使用Angular 2中的FormArray和ControlValueAccessor

21

我有一个子组件处理输入控件的数组。 我想在子组件上拥有一个FormControl。

我正在传递json对象的数组,将父表单绑定到具有2个FormControl的子组件的FormArray的正确方法是什么,第一个FormControl需要带有“必选项”验证器。

这是初始代码。

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

意图是将父表单与子组件中的输入控件数组绑定在一起。如果子组件中的一个输入控件缺少必填字段,则表单也将变为无效。

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


你能澄清一下将用户名表单控件设置为JSON对象的意图是什么吗?您是否试图在子组件中列出JSON中每个名字姓氏对的名字和姓氏? - silentsod
是的,根据给定的JSON,我正在尝试在输入控件中呈现每个对。 - Anonymous Me
1
@AnonymousMe,你找到这个问题的解决方案了吗? - Junaid
使用 ngx-sub-form 很容易做到这一点,请参见 https://github.com/cloudnc/ngx-sub-form#dealing-with-arrays - maxime1992
2个回答

10
我喜欢解决旧帖子 :)
关键是您的自定义表单组件内有一个FormArray,然后使用"writeValue"来创建FormArray,请参见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" };
    }
}

1
你需要使用formArrayName指令和*ngFor来实现如下效果:
<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>

使用FormBuilder时,您还需要使用FormArray。
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"}
                ])
   });

如需更多详细信息,请查看此文档。

情况2:传递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')
                  }),
                ])
   })

如果你试图将FormGroup作为ngModel参数传递,那是不可能的!


2
这在父组件中创建formArray时是有效的,但我希望有一个包含formArray的子组件。 - Anonymous Me
您的思考方式是错误和反模式的。问题是:为什么要将FormGroup嵌入到一个新组件中?这不是一个好的方法。但是,如果您坚持这样做,可以创建一个获取FormGroup并在内部使用它的组件。 - Serginho
6
这怎么可能是反模式?子组件必须存放在库中,以便多个页面使用。与其到处复制粘贴代码,不如创建一个子组件,在多个地方调用它。 - Anonymous Me
你只能向ngModel传递一个参数,不能将两个参数转换为一个参数。这个组件永远不会与ngModel一起使用,无论是你的应用程序还是组件本身。 - Serginho

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接