具有嵌套组件的Angular 2 Reactive Form

时间:2018-06-06 02:27:05

标签: angular typescript angular-reactive-forms

基于这2个网址:

我想:

  • a MasterComponent
  • with ListComponent
  • 和ListItemComponent

MasterComponent.ts

  public data: Data;
  public form: FormGroup = this.fb.group({});

  formInitialized(name: string, form: FormGroup) {
    this.form.addControl(name, form);
  }

MasterComponent.html

  <app-list [parentForm]="form"
        [items]="data.items"
        (formReady)="formInitialized('items', $event)"></app-list>

ListComponent.ts

  @Input() parentForm: FormGroup;
  @Input() items: item[];
  @Output() formReady = new EventEmitter<AbstractControl>()

  public itemsForm: FormArray;

  formInitialized(itemForm: FormGroup) {
    debugger;
    this.itemsForm.push(itemForm);
  }

  ngOnInit() {
    debugger;
    this.itemsForm = new FormArray([]);
    this.formReady.emit(this.itemsForm);
  }

ListComponent.html

    <div [formGroup]="parentForm">
      <div formArrayName="items">
        <ng-template let-item let-last="last" let-i="index" ngFor [ngForOf]="items">
          <app-item [itemsForm]="parentForm.controls.items"
                         [item]="item"
                         (formReady)="formInitialized($event)">
      </app-item>
    </ng-template>
  </div>
</div>

item.component.ts

  @Input() itemsForm: FormArray;
  @Input() item: Item;
  @Output() formReady = new EventEmitter<AbstractControl>()

  public itemForm: FormGroup;
  ngOnInit() {
    this.itemForm = this._formBuilder.group({title: [item.title || '', Validators.required],});
    this.formReady.emit(this.itemForm);
  }

item.component.html

<div class="md-padding" [formGroup]="itemForm">
 <mat-form-field flex>
        <input #title
               name="title"
               matInput
               type="text"
               placeholder="Title"
               formControlName="title"
               maxlength="150"
               required />
        <mat-error *ngIf="title.pristine || !title.errors">
          <div *ngIf="title.errors?.required">
            this field is required.
          </div>
          <div *ngIf="title.errors?.maxlength">
            max length is 150.
          </div>
        </mat-error>
        <mat-hint align="end">{{title.value.length}} / 150</mat-hint>
      </mat-form-field>
</div>

我的代码有错误:

MasterComponent.html:12 ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'Items: [object Object],[object Object]core.js:8445)
    at expressionChangedAfterItHasBeenCheckedError (core.js:8433)
    at checkBindingNoChanges (core.js:8535)
    at checkNoChangesNodeInline (core.js:11403)
    at checkNoChangesNode (core.js:11390)
    at debugCheckNoChangesNode (core.js:11997)
    at debugCheckDirectivesFn (core.js:11925)
    at Object.eval [as updateDirectives] (MasterComponent.html:12)
    at Object.debugUpdateDirectives [as updateDirectives] (core.js:11914)
    at checkNoChangesView (core.js:11289)

我喜欢子组件负责将formGroup提供给主组件1的概念。 解决方案1的问题是子组件无法更新master组件(请参阅问题https://github.com/brophdawg11/ng-playground/issues/5)。 因此,我使用解决方案1修改了解决方案2

但它不起作用。有没有办法解决这个问题?或者你有一个很好的教程提议吗?

由于

1 个答案:

答案 0 :(得分:3)

您可以只关注Brophy's example ...

创建formControl / formGroup时,不是让formReady发出, 而是只需通过@input传递父项将其添加到父项表单中。

诀窍是在正确的索引处更新表单,而不仅仅是推送到formArray ...

例如,

父母:(TS)

styleForm: FormGroup;
@Input() style: IStyle;

constructor(private fb: FormBuilder) {}

ngOnInit(): void {
  this.styleForm = this.buildStyleForm();
}


buildStyleForm() {
  return this.fb.group({
    selected: false,
    products: this.fb.array([])
  });
}

父母:(HTML)

<ul class="style_product-list" formArrayName="products">
  <li class="style_product" *ngFor="let product of style?.products; index as i">
    <product [styleForm]="styleForm" [productIndex]="i" [product]="product"></product>
  </li>
</ul>

孩子:

products: FormArray;
@Input() styleForm: FormGroup;
@Input() product: IProduct;
@Input() productIndex: number;

ngOnInit(): void {
  this.productForm = this.buildProductFormGroup(this.product);
  this.products = this.styleForm.controls.products as FormArray;
  this.pushGroupIntoArray(this.products, this.productForm, this.productIndex);
}

pushGroupIntoArray(array: FormArray, group: FormGroup, index: number) {
  if (array.at(index)) {
    array.setControl(index, group);
  } else {
    array.push(group);
  }
}

buildProductFormGroup(product: IProduct): FormGroup {
  return <FormGroup > this.fb.group({
    selected: [product && product.selected],
    quantity: [product && product.quantity, Validators.required],
    parts: [product && product.parts, Validators.required]
  });
}

这会将更新的formControl / formGroup交给父级(它将被链接!)