【问题标题】:angular2 (2.0.0) repeater using reactive forms and templatesangular2 (2.0.0) 使用响应式表单和模板的转发器
【发布时间】:2016-12-19 13:04:39
【问题描述】:

我正在尝试做的事情:我正在构建一个可重用的 FormGroups 转发器(反应式表单),其中转发器的通用部分提供添加/删除控件和逻辑以及所有想要的组件使用它只需要提供必须重复的模板。我查过了

它展示了如何以模板驱动的方式完成类似的事情

我卡住的地方:我附加到<template> 中的控件的formControlName 指令没有被转发器的通用部分拾取:

Error: Cannot find control with name: 'id'
at _throwError (https://unpkg.com/@angular/forms/bundles/forms.umd.js:1527:15)
at setUpControl (https://unpkg.com/@angular/forms/bundles/forms.umd.js:1465:13)
at FormGroupDirective.addControl (https://unpkg.com/@angular/forms/bundles/forms.umd.js:3889:13)
at FormControlName._setUpControl (https://unpkg.com/@angular/forms/bundles/forms.umd.js:4318:48)
at FormControlName.ngOnChanges (https://unpkg.com/@angular/forms/bundles/forms.umd.js:4264:22)

代码: (plunkr)

RepeaterComponent2

@Component({
    selector: 'repeater2',
    template: `
        <div *ngIf="formArray">debug formArray: {{formArray.length}}</div>
        <div [formGroup]="formGroup">
            <input type="button" value="add" (click)="addRow()" noBootstrap>
            <div formArrayName="values" class="repeater" *ngFor="let row of model; let i = index">
                <div [formGroupName]="i" class="repeaterRow">
                    <input type="button" value="remove" (click)="removeRow(row, i)"  noBootstrap>
                    <template [ngTemplateOutlet]="itemTemplate" [ngOutletContext]="{item: row}"></template>
                </div>
            </div>
        </div>
    `
})
export class RepeaterComponent2 implements OnInit {
    /* initial model */
    @Input() model: any[] = [];
    /* function to create a new item */
    @Input() newItem: (i) => {};
    /* function to create a new FormGroup to fit the item */
    @Input() newItemFormGroup: (fb: FormBuilder, model: any) => FormGroup;
    /* root form group where the local FormArray is attached to */
    @Input() formGroup: FormGroup;    

    @Output() repeaterArrayChanged: EventEmitter<any[]>;
    @ContentChild(TemplateRef) itemTemplate: TemplateRef<any>;    

    formArray: FormArray;    

    constructor(private fb: FormBuilder) {
        this.repeaterArrayChanged = new EventEmitter<any[]>();
    }    

    ngOnInit() {
        this.formArray = this.fb.array([]);
        this.formGroup.addControl('values', this.formArray);
        this.model.forEach((item: any) => this.addItemFormGroup(item))
    }    

    addRow() {
        let id = this.model.length + 1;
        let obj = this.newItem(id);
        this.model.push(obj);
        this.addItemFormGroup(obj);
        this.repeaterArrayChanged.emit(this.model);
    }    

    addItemFormGroup(item: any) {
        this.formArray.push(this.newItemFormGroup(this.fb, item));
    }    

    removeItemFormGroup(i) {
        this.formArray.removeAt(i);
    }    

    removeRow(row, i) {
        this.model = this.model.filter((x: any) => x !==row);
        this.removeItemFormGroup(i);
        this.repeaterArrayChanged.emit(this.model);
    }
}

应该如何使用:

    @Component({
    selector: 'my-app',
    template: `
        <div>
            <div>{{debugForm | json}}</div>
            <form [formGroup]="formGroup">
                <!-- passing in:
                    - the root form group (the formArray of the repeater adds itself to it)
                    - the initial model items
                    - a callback that is used to create a new model item when "add" is clicked 
                    - a callback that is used to create a "suitable" FormGroup for the passed model item 
                -->
                <repeater2 [formGroup]="formGroup" [model]="model.items" [newItem]="newItem" [newItemFormGroup]="newItemFormGroup">
                    <template let-row="item">
                        <span>{{row | json}}</span>
                        <!-- !!! THIS FORM CONTROL IS NOT PICKED UP !!! -->
                        <input type="text" formControlName="id" noBootstrap>
                    </template>
                </repeater2>
            </form>
        </div>
    `
})
export class RepeaterTestComponent2 implements OnInit {    

    model: Model;
    formGroup: FormGroup;    

    constructor(private fb: FormBuilder) {
        this.model = new Model(1, [
            new Item(1, 'one'),
            new Item(2, 'two'),
            new Item(3, 'three')
        ]);
    }    

    ngOnInit() {
        this.formGroup = this.fb.group({});
    }    

    newItem(i): any {
        return new Item(i, 'xxx');
    }    

    newItemFormGroup(fb: FormBuilder, model: any): any {
        return fb.group({
            id: []
        });
    }    

    get debugForm() {
        return {
            value: this.formGroup.value
        };
    }
}

【问题讨论】:

    标签: angular angular2-forms


    【解决方案1】:

    是的,根据下面链接的类似问题,这是可能的。

    基本上,您的元素必须实现ngModelformControlName 注入的ControlValueAccessor 接口,如下所示:

    @Directive({
      selector: 'control',
      providers: [
        {provide: NG_VALUE_ACCESSOR, multi: true, useExisting: SwitchControlComponent}
      ]
    })
    export class SwitchControlComponent implements ControlValueAccessor {
      isOn: boolean;
      _onChange: (value: any) => void;
    
      writeValue(value: any) {
        this.isOn = !!value;
      }
    
      registerOnChange(fn: (value: any) => void) {
        this._onChange = fn;
      }
    
      registerOnTouched() {}
    
      toggle(isOn: boolean) {
        this.isOn = isOn;
        this._onChange(isOn);
      }
    }
    

    请注意,您必须 (AFAIK) 然后将表单传递给嵌入的模板 — 请参阅 let-form="form"

    <repeater2 [formGroup]="formGroup" [model]="model.items" [newItem]="newItem" [newItemFormGroup]="newItemFormGroup">
        <template let-row="item" let-form="form">
            <span>{{row | json}}</span>
            <input control type="text" formControlName="id" noBootstrap>
        </template>
    </repeater2>
    

    延伸阅读:

    Angular2: Bind form context to ngTemplateOutlet

    With new forms api, can't add inputs from child components without adding additional form tags

    【讨论】:

      猜你喜欢
      • 2019-01-23
      • 2021-05-13
      • 2019-11-07
      • 2018-05-13
      • 1970-01-01
      • 2016-12-02
      • 1970-01-01
      • 2019-06-28
      • 1970-01-01
      相关资源
      最近更新 更多