在我的用例中,当用户单击编辑按钮时,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>
ProductEditComponent
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来更新FormControls。
对于你的情况,我会推荐patchValue
,因为它比setValue
更灵活。 patchValue
不要求在参数中指定所有FormControls以更新/设置表单控件的值。
这是你如何使用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
获取数据时,可以使用在FormGroup和FormControls上公开的value
属性。
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']);
});
}