angular - 未触及字段时,Angular 反应式表单返回空值
问题描述
在我的用例中,当用户单击编辑按钮时,Angular 对后端进行 HTTP 调用并检索对象,然后在 EDIT 表单上填充这些值。用户可以更新或保持字段不变。单击update
按钮时,Angular 应该获取表单中存在的所有这些值并将它们发送到后端。因此,问题是,在将值加载到编辑页面表单并更新某些字段并保持某些字段未触及后,未触及的值会变为空。这真的很奇怪
产品-edit.component.html
<div *ngIf="productDataAvailable()">
<h2>Update Product</h2>
<br/>
<form [formGroup]="productForm">
<div class="form-group">
<label for="id">Product Id</label>
<input class="form-control" formControlName="id" id="id" type="text" value="{{product.id}}">
<small class="form-text text-muted" id="emailHelp"></small>
</div>
<div class="form-group">
<label for="name">Name</label>
<input class="form-control" formControlName="name" id="name" type="text" value="{{product.name}}">
</div>
<div class="form-group">
<label for="description">Description</label>
<input class="form-control" formControlName="description" height="100" id="description" required type="text"
[value]="product.description" width="200">
</div>
<div class="form-group">
<label for="currency">Price</label> <br/>
<label>
<select (load)="loadCurrencies()" class="form-control" [value]="product.price.currency.symbol" formControlName="currency" id="currency" name="currency">
<option *ngFor="let currency of currencies" value={{currency.id}}>
{{currency.name}}
</option>
</select>
</label>
<input formControlName="price" id="price" required style="margin: 10px; padding: 5px" type="text" [value]="product.price.amount">
</div>
<div class="form-group">
<label>Category:
<select (load)="loadCategories()" class="form-control" formControlName="category" name="category">
<option [value]="category.id" *ngFor="let category of categories">
{{category.name}}
</option>
</select>
</label>
</div>
<div class="form-group">
<label>Manufacturer:
<select (load)="loadManufacturers()" class="form-control" [value]="product.manufacturer.name" formControlName="manufacturer" name="manufacturer">
<option [value]="manufacturer.id" *ngFor="let manufacturer of manufacturers" >
{{manufacturer.name}}
</option>
</select>
</label>
</div>
<button (click)="updateProduct()" class="btn btn-primary" type="submit">Submit</button>
<button (click)="goBack()" class="btn btn-primary" style="margin-left: 30px" type="button">Cancel</button>
</form>
</div>
产品编辑组件
import {Component, OnInit} from '@angular/core';
import {Product} from '../model/product';
import {ProductService} from '../service/product.service';
import {ActivatedRoute, Router} from '@angular/router';
import {CATEGORY_API_URL, CURRENCY_API_URL, MANUFACTURER_API_URL, PRODUCT_API_URL, SERVER_URL} from '../../../app.constants';
import {FormControl, FormGroup, Validators} from '@angular/forms';
import {Price} from '../model/price';
import {Currency} from '../model/currency';
import {Category} from '../../category/model/category';
import {Manufacturer} from '../model/manufacturer';
import {CategoryService} from '../../category/service/category.service';
@Component( {
selector: 'app-product-edit',
templateUrl: './product-edit.component.html',
styleUrls: ['./product-edit.component.css']
} )
export class ProductEditComponent implements OnInit
{
product: Product;
categories: Array<Category>;
currencies: Array<Currency>;
manufacturers: Array<Manufacturer>;
productForm=new FormGroup( {
id: new FormControl( {value: '', disabled: true}, Validators.minLength( 2 ) ),
name: new FormControl( '' ),
description: new FormControl( '' ),
price: new FormControl( '' ),
category: new FormControl( '' ),
currency: new FormControl( '' ),
manufacturer: new FormControl( '' )
} );
constructor(private productService: ProductService,
private categoryService: CategoryService,
private route: ActivatedRoute,
private router: Router)
{
}
ngOnInit()
{
this.getProduct();
this.loadCategories();
this.loadCurrencies();
this.loadManufacturers();
}
productDataAvailable(): boolean
{
return this.product!==undefined;
}
goBack()
{
this.router.navigate( ['/product'] );
}
private getProduct()
{
const id=this.route.snapshot.paramMap.get( 'id' );
const url=SERVER_URL+PRODUCT_API_URL+'find/'+id;
this.productService.getProductDetails( url ).pipe()
.subscribe(
data =>
{
this.product=data;
},
error =>
{
console.log( error );
},
() => console.log( 'getProduct() success' ) );
}
private updateProduct()
{
const id=this.route.snapshot.paramMap.get( 'id' );
const url=SERVER_URL+PRODUCT_API_URL+'update';
const product=new Product();
product.id=Number( id );
product.name=this.productForm.get( 'name' ).value;
product.description=this.productForm.get( 'description' ).value;
const currency=new Currency( this.productForm.get( 'currency' ).value, 'USD', '$' );
product.price=new Price(currency , this.productForm.get( 'price' ).value );
product.category=new Category( this.productForm.get( 'category' ).value );
product.manufacturer=new Manufacturer( this.productForm.get( 'manufacturer' ).value );
product.lastModifiedBy='Admin';
product.lastModifiedDate='Admin';
this.productService.updateProduct( url, product ).subscribe(
value =>
{
console.log( 'Successfully updated product' );
}, error1 =>
{
console.log( 'Failed to update product' );
},
() =>
{
this.router.navigate( ['/product/list'] );
} );
}
private loadCategories()
{
const url=SERVER_URL+CATEGORY_API_URL+'list';
this.categoryService.getCategories( url ).subscribe(
categories =>
{
// @ts-ignore
this.categories=categories;
console.log( 'Successfully loaded categories' );
},
error1 =>
{
console.log( 'Failed to load categories' );
},
() =>
{
} );
}
private loadCurrencies()
{
const url=SERVER_URL+CURRENCY_API_URL+'list';
this.productService.getCurrencies( url ).subscribe(
currencies =>
{
this.currencies=currencies;
},
error1 =>
{
console.log( 'Failed to load currencies' );
},
() =>
{
} );
}
private loadManufacturers()
{
const url=SERVER_URL+MANUFACTURER_API_URL+'list';
this.productService.getManufacturers( url ).subscribe(
manufacturers =>
{
this.manufacturers=manufacturers;
console.log( 'Successfully loaded manufacturers' );
},
error1 =>
{
console.log( 'Failed to load manufacturers' );
},
() =>
{
} );
}
}
角度版本
Angular CLI: 7.3.8
Node: 10.15.0
OS: darwin x64
Angular: 7.2.12
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, router
Package Version
-----------------------------------------------------------
@angular-devkit/architect 0.13.8
@angular-devkit/build-angular 0.13.8
@angular-devkit/build-optimizer 0.13.8
@angular-devkit/build-webpack 0.13.8
@angular-devkit/core 7.3.8
@angular-devkit/schematics 7.3.8
@angular/cli 7.3.8
@ngtools/webpack 7.3.8
@schematics/angular 7.3.8
@schematics/update 0.13.8
rxjs 6.4.0
typescript 3.2.4
webpack 4.29.0
解决方案
据我所知,您已发出 HTTP 请求以从服务器获取数据,但您没有productForm
以正确的方式填充 FormGroup。由于您使用的是响应式表单,我强烈建议您使用patchValue或setValue来更新您的 FormControl。
对于您的情况,我建议您使用patchValue
,因为它比setValue
. patchValue
不需要在参数中指定所有 FormControls 以更新/设置 Form Controls 的值。
这就是您可以使用patchValue 的方式。在您的getProduct()
方法中,您可以通过执行此操作将data
响应中的属性传递getProductDetails()
到您的 FormGroup 中;
getProduct() {
const id = this.route.snapshot.paramMap.get('id');
const url = SERVER_URL + PRODUCT_API_URL + 'find/' + id;
this.productService.getProductDetails(url).pipe()
.subscribe(
data => {
this.productForm.patchValue({
id: data.id
name: data.name
description: data.description
// other form fields
})
},
error => {
console.log(error);
},
() => console.log('getProduct() success'));
}
此外,在您的模板 html 上,无需在每个或上绑定您的value
属性。您可以删除所有这些。这是因为,您已经在使用 更新值。<input>
<select>
patchValue
<div class="form-group">
<label for="name">Name</label>
<input class="form-control" formControlName="name" id="name" type="text">
</div>
<div class="form-group">
<label for="description">Description</label>
<input class="form-control" formControlName="description" height="100" id="description" required type="text" width="200">
</div>
当您需要从 中获取数据时productForm
,您可以使用value
在您的 FormGroup 和 FormControls 上公开的属性。
updateProduct() {
const id = this.route.snapshot.paramMap.get('id');
const url = SERVER_URL + PRODUCT_API_URL + 'update';
//console.log(this.productFrom.value)
const product = this.productForm.value
this.productService.updateProduct(url, product).subscribe(
value => {
console.log('Successfully updated product');
}, error1 => {
console.log('Failed to update product');
},
() => {
this.router.navigate(['/product/list']);
});
}
推荐阅读
- c - 如何将任何大数字转换/截断为 C 中的前 3 位数字?
- python - 我有一个客户端代码和一个 python 中的服务器代码。服务器启动但客户端找不到
- c# - .NET IQueryable OrderBy 聚合字段
- javascript - 从父级访问反应组件的属性
- python - 如何在 if 标签 django 中使用自定义标签?
- html - 使某些对象在点击时不可见,仅使用 HTML 和 CSS,无需 JS 或 jQuery
- mysql - Docker 设置 mysql 数据库
- python - Python根据maxchars字符串长度边界返回要打印的切片列表
- postgresql - 使用行级安全性 (RLS) 时如何使 INSERT ... RETURNING 语句起作用?
- python-3.x - 导入失败并出现 KeyError: 'id'