formarray的formcontrol未在mattable中更新

时间:2018-08-18 12:13:00

标签: angular angular-material angular-reactive-forms formarray formgroups

我在使用材质表和表格的Angular中使用反应式表格时遇到了一个奇怪的问题。 我有一个带有自动完成设置的表单,该表单可以从api获取产品。我在onClick上调用了一个函数,该函数以默认数量为1的形式将产品添加到表单数组中。如果用户再次单击同一产品,我将增加数量,因此不会出现两次仅增加数量的产品。我还启用了数量作为输入,因此,如果用户要手动输入数量,则只需输入值即可。 问题在于它的值在窗体的值中增加了,但在窗体控件的值中却没有增加。表格的json输出显示2,但输入字段显示1。查看代码和附加图像以更好地理解。

enter image description here

enter image description here

add.deal.component.ts

import {Component, OnInit, ViewChild} from '@angular/core';
import {AbstractControl, FormArray, FormBuilder, FormControl, FormGroup, NgForm, Validators} from "@angular/forms";
import {DealService} from "../deal.service";
import {Deal} from "../deal";
import {BehaviorSubject, Observable} from "rxjs/Rx";
import {Product} from "../../product/product.interface";
import {ProductService} from "../../product/product.service";
import {map, startWith} from "rxjs/operators";
import {MatPaginator, MatSort, MatTableDataSource} from "@angular/material";
import {DealProducts} from "../deal-products";

const ELEMENT_DATA: DealProducts[] = [];

@Component({
  selector: 'app-add-deal',
  templateUrl: './add-deal.component.html',
  styleUrls: ['./add-deal.component.css']
})
export class AddDealComponent implements OnInit
{

  dealForm: FormGroup;

  filteredProducts: Observable<Product[]>;
  products: Product[] = [];
  dealProducts: FormArray = new FormArray([]);

  displayedColumns = ['name', 'description', 'quantity'];
  // dataSource = new MatTableDataSource(ELEMENT_DATA);
  // dataSource = new BehaviorSubject;
  dataSource = new BehaviorSubject<AbstractControl[]>([]);
  loading: boolean = false;

  @ViewChild(MatPaginator) paginator: MatPaginator;
  @ViewChild(MatSort) sort: MatSort;

  constructor (private productService: ProductService, private formBuilder: FormBuilder, private dealService: DealService) {}

  ngOnInit ()
  {
    this.initForm();
    this.productService.getProducts().subscribe(
      (data: Product[]) =>
      {
        console.log(data);
        this.products = data;
        this.filteredProducts = this.dealForm.get('product').valueChanges
          .pipe(
            startWith<string | Product>(''),
            map(value => typeof value === 'string' ? value : value.name),
            map(name => name ? this._filterProducts(name) : this.products.slice())
          );
      }
    );
  }

  initForm ()
  {
    this.dealForm = this.formBuilder.group({
      'name': [null, Validators.required],
      'description': [null, Validators.required],
      'price': [null, Validators.required],
      'product': [null],
      'dealProducts': this.dealProducts
    });
    // this.dealProducts.push(
    //   new FormGroup({
    //     product: new FormControl('', Validators.required),
    //     quantity: new FormControl(1, Validators.required)
    //   })
    // );
  }

  createDealProduct (): FormGroup
  {
    return this.formBuilder.group({
      product: null,
      quantity: 1
    });
  }

  onProductSelect (product: Product)
  {
    this.dealForm.get('product').setValue('');
    if (this.dealForm.value.dealProducts.length > 0)
    {
      if (this.productExists(product.id))
      {
        for (const item of this.dealForm.value.dealProducts)
        {
          if (item.product.id === product.id)
          {
            item.quantity++;
          }
        }
      }
      else
      {
        (<FormArray>this.dealForm.controls['dealProducts']).push(
          new FormGroup({
            product: new FormControl(product),
            quantity: new FormControl(1, Validators.required)
          })
        );
      }

      // this.dataSource = (<MatTableDataSource>(<FormArray>this.dealForm.controls['dealProducts']).controls);
      this.dataSource.next((<FormArray>this.dealForm.controls['dealProducts']).controls);
      // this.dataSource.next(this.dealForm.value.dealProducts);

    }
    else
    {
      (<FormArray>this.dealForm.controls['dealProducts']).push(
        new FormGroup({
          product: new FormControl(product),
          quantity: new FormControl(1, Validators.required)
        })
      );
      // this.dataSource = (<MatTableDataSource>(<FormArray>this.dealForm.controls['dealProducts']).controls);

      // this.dataSource.next((<FormArray>this.dealForm.controls['dealProducts']).controls);
      // this.dataSource.next(this.dealForm.value.dealProducts);
      this.dataSource.next((<FormArray>this.dealForm.controls['dealProducts']).controls);

    }
    console.log(this.dealForm);
  }

  productExists (id)
  {
    for (const item of this.dealForm.value.dealProducts)
    {
      if (item.product.id === id)
      {
        return true;
      }
    }
    return false;
  }

  displayProduct (product ?: Product): string | undefined
  {
    return product ? product.name : undefined;
  }

  private
  _filterProducts (name: string): Product[]
  {
    const filterValue = name.toLowerCase();

    return this.products.filter(product => product.name.toLowerCase().indexOf(filterValue) === 0);
  }

  getErrorMessage ()
  {
    for (const field in this.dealForm.controls)
    {
      if (!this.dealForm.get(field).valid && this.dealForm.get(field).touched)
      {
        return 'Please enter a value';
      }
    }
  }

  onFormSubmit (form)
  {

    let myDeal: Deal;
    myDeal.name = form.name;
    myDeal.description = form.description;
    myDeal.price = form.price;
    myDeal.dealProducts = form.dealProducts;
    console.log(myDeal);
    // const jsonString = JSON.stringify(form);
    // const deal = <Deal>JSON.parse(jsonString);
    // console.log(deal);
    // this.dealService.createDeal(deal).subscribe(
    //   data => {
    //     console.log(data);
    //   },
    //   error => {
    //     console.log(error);
    //   }
    // );
  }
}

add.deal.component.html

<div fxLayout="row" fxLayoutWrap="wrap">
  <div fxFlex.gt-sm="100" fxFlex.gt-xs="100" fxFlex="100">
    <mat-card>
      <mat-toolbar color="primary">
        <span>Add Deal</span>
        <span fxFlex></span>
      </mat-toolbar>
    </mat-card>
  </div>
</div>
<div fxLayout="row" fxLayoutWrap="wrap">
  <div fxFlex.gt-sm="100" fxFlex.gt-xs="100" fxFlex="100">
    <mat-card>
      <mat-card-content class="mat-elevation-z8">
        <form [formGroup]="dealForm" (ngSubmit)="onFormSubmit(dealForm.value)">
          <div fxLayout="row" class="row" fxflexalign="center" fxLayoutWrap="wrap" ng-reflect-layout="row"
               ng-reflect-wrap="wrap" ng-reflect-align="center"
               style="flex-flow: row wrap; box-sizing: border-box; display: flex; align-self: center;">
            <div class="p-10" fxflex="100" fxflex.gt-sm="35" ng-reflect-flex="100" ng-reflect-flex-gt-sm="35"
                 style="flex: 1 1 35%; box-sizing: border-box; max-width: 35%;">
              <mat-form-field>
                <input matInput placeholder="Name" formControlName="name" required>
                <mat-error *ngIf="dealForm.get('name').invalid">{{getErrorMessage()}}</mat-error>
              </mat-form-field>
            </div>
            <div class="p-10" fxflex="100" fxflex.gt-sm="35" ng-reflect-flex="100" ng-reflect-flex-gt-sm="35"
                 style="flex: 1 1 35%; box-sizing: border-box; max-width: 35%;">
              <mat-form-field>
                <input matInput type="number" placeholder="Price" formControlName="price" required>
                <mat-error *ngIf="dealForm.get('price').invalid">{{getErrorMessage()}}</mat-error>
              </mat-form-field>
            </div>
            <div class="p-10" fxflex="100" fxflex.gt-sm="30" ng-reflect-flex="100" ng-reflect-flex-gt-sm="30"
                 style="flex: 1 1 30%; box-sizing: border-box; max-width: 30%;">
              <mat-form-field>
                <textarea matInput placeholder="Description" formControlName="description" required></textarea>
                <mat-error *ngIf="dealForm.get('description').invalid">{{getErrorMessage()}}</mat-error>
              </mat-form-field>
            </div>
            <div class="p-10" fxflex="100" fxflex.gt-sm="65" ng-reflect-flex="100" ng-reflect-flex-gt-sm="65"
                 style="flex: 1 1 65%; box-sizing: border-box; max-width: 65%;">
              <mat-form-field class="example-full-width">
                <input matInput placeholder="Search Product" aria-label="Products" [matAutocomplete]="autoProduct"
                       formControlName="product">
                <mat-autocomplete #autoProduct="matAutocomplete" [displayWith]="displayProduct">
                  <mat-option *ngFor="let product of filteredProducts | async" [value]="product"
                              (onSelectionChange)="onProductSelect(product)">
                    <!--<img class="example-option-img" aria-hidden [src]="state.flag" height="25">-->
                    <span>{{product.name}}</span>
                    <!--<small>Population: {{state.population}}</small>-->
                  </mat-option>
                </mat-autocomplete>
                <mat-error *ngIf="dealForm.get('product').invalid">{{getErrorMessage()}}</mat-error>
              </mat-form-field>
            </div>
            <div class="p-10" fxflex="100" fxflex.gt-sm="35" ng-reflect-flex="100" ng-reflect-flex-gt-sm="35"
                 style="flex: 1 1 35%; box-sizing: border-box; max-width: 35%;">
              <button class="btn-block" mat-raised-button color="primary" [disabled]="dealForm.invalid">
                Save
              </button>
            </div>
          </div>
          <div>
            <mat-table #table [dataSource]="dataSource" formArrayName="dealProducts">
              <ng-container matColumnDef="name">
                <mat-header-cell *matHeaderCellDef> Name</mat-header-cell>
                <mat-cell *matCellDef="let element; let i = index;" formGroupName="{{i}}"> {{ element.controls.product.value.name }}</mat-cell>
              </ng-container>
              <ng-container matColumnDef="description">
                <mat-header-cell *matHeaderCellDef> Description</mat-header-cell>
                <mat-cell *matCellDef="let element; let i = index;" formGroupName="{{i}}"> {{ element.controls.product.value.description }}</mat-cell>
              </ng-container>
              <ng-container matColumnDef="quantity">
                <mat-header-cell *matHeaderCellDef> Quantity</mat-header-cell>
                <mat-cell *matCellDef="let element; let i = index;" formGroupName="{{i}}">
                  <mat-form-field>
                    <input matInput type="number" formControlName="quantity" required>
                    <mat-error *ngIf="dealForm.get('price').invalid">{{getErrorMessage()}}</mat-error>
                  </mat-form-field>
                </mat-cell>
              </ng-container>
              <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
              <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
            </mat-table>
          </div>
        </form>
        <pre>
          {{dealForm.value |json}}
        </pre>
      </mat-card-content>
    </mat-card>
  </div>
</div>

1 个答案:

答案 0 :(得分:0)

我也遇到了类似的问题,即表单控件未在mat-table中更新。看起来像个错误,但我认为您可以通过使用[formGroup]而不是formGroupName引用formControl来解决此问题。在您的示例中:

<input matInput type="number" [formControl]="dealForm.get('dealProducts').controls[i].get('quantity')" required>