我在使用材料表和表单的 Angular 中使用反应表单时遇到了一个奇怪的问题。我有一个带有自动完成设置的表单,可以从 api 获取产品。我在 onClick 上调用一个函数,该函数将该产品添加到默认数量为 1 的表单数组中。如果用户再次单击同一产品,我将增加数量,因此没有产品出现两次,只有数量增加。我还启用了数量作为输入,这样如果用户想手动输入数量,他只需输入值即可。问题是它在表单的值中增加,但在表单控件的值中没有增加。表单的 json 输出显示 2,但输入字段显示 1。查看代码和附加图像以更好地理解。
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>