首页
其他开发
当字段未触及时,Angular 反应形式返回空值
当字段未触及时,Angular 反应形式返回空值
[英] Angular reactive form returns empty values when fields are untouched
本文介绍了当字段未触及时,Angular 反应形式返回空值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
在我的用例中,当用户单击编辑按钮时,Angular 对后端进行 HTTP 调用并检索对象,然后在 EDIT 表单上填充这些值.用户可以更新或保持字段不变.当点击 update
按钮时,Angular 应该获取表单中存在的所有这些值并将它们发送到后端.所以,这是问题所在,在将值加载到编辑页面表单并更新某些字段并保留某些字段不变后,未修改的值会变为空.这真的很奇怪
product-edit.component.html
<h2>更新产品</h2><br/><form [formGroup]="productForm"><div class="form-group"><label for="id">产品 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 class="form-group"><label for="name">Name</label><input class="form-control" formControlName="name" id="name" type="text" value="{{product.name}}">
<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 class="form-group"><label for="currency">价格</label><br/><标签><select (load)="loadCurrencies()" class="form-control" [value]="product.price.currency.symbol" formControlName="currency" id="currency" name="currency"><option *ngFor="让货币的货币" value={{currency.id}}>{{货币名称}}</选项></选择>标签><input formControlName="price" id="price" required style="margin: 10px; padding: 5px" type="text" [value]="product.price.amount">
<div class="form-group"><标签>类别:<select (load)="loadCategories()" class="form-control" formControlName="category" name="category"><option [value]="category.id" *ngFor="let category of category">{{分类名称}}</选项></选择>标签>
<div class="form-group"><标签>制造商:<select (load)="loadManufacturers()" class="form-control" [value]="product.manufacturer.name" formControlName="manufacturer" name="manufacturer"><option [value]="manufacturer.id" *ngFor="让制造商的制造商" >{{生产商名称}}</选项></选择>标签>
<button (click)="updateProduct()" class="btn btn-primary" type="submit">Submit</button><button (click)="goBack()" class="btn btn-primary" style="margin-left: 30px" type="button">取消</button></表单>
ProductEditComponent
import {Component, OnInit} from '@angular/core';从'../model/product'导入{产品};从'../service/product.service'导入{ProductService};从@angular/router"导入 {ActivatedRoute, Router};从../../../app.constants"导入 {CATEGORY_API_URL, CURRENCY_API_URL, MANUFACTURER_API_URL, PRODUCT_API_URL, SERVER_URL};从@angular/forms"导入 {FormControl、FormGroup、Validators};从 '../model/price' 导入 {Price};从'../model/currency'导入{Currency};从../../category/model/category"导入{类别};从'../model/manufacturer'导入{制造商};从'../../category/service/category.service'导入{CategoryService};@成分( {选择器:'app-product-edit',templateUrl: './product-edit.component.html',styleUrls: ['./product-edit.component.css']})导出类 ProductEditComponent 实现 OnInit{产品:产品;类别:数组<类别>;货币:数组<货币>;制造商:Array<制造商>;产品表格=新表格组({id: new FormControl( {value: '', disabled: true}, Validators.minLength( 2 ) ),名称:new FormControl( '' ),描述:new FormControl( '' ),价格:新的FormControl(''),类别:new FormControl( '' ),货币:new FormControl(''),制造商: new FormControl( '' )});构造函数(私有产品服务:产品服务,私有类别服务:类别服务,私人路线:ActivatedRoute,专用路由器:路由器){}ngOnInit(){this.getProduct();this.loadCategories();this.loadCurrencies();this.loadManufacturers();}productDataAvailable(): 布尔值{返回 this.product!==undefined;}回去(){this.router.navigate( ['/product'] );}私有 getProduct(){const id=this.route.snapshot.paramMap.get('id');const url=SERVER_URL+PRODUCT_API_URL+'find/'+id;this.productService.getProductDetails( url ).pipe().订阅(数据 =>{this.product=数据;},错误 =>{控制台日志(错误);},() =>console.log('getProduct() 成功'));}私人更新产品(){const id=this.route.snapshot.paramMap.get('id');const url=SERVER_URL+PRODUCT_API_URL+'更新';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='管理员';product.lastModifiedDate='管理员';this.productService.updateProduct( url, product ).subscribe(值 =>{console.log('产品更新成功');}, error1 =>{console.log('更新产品失败');},() =>{this.router.navigate( ['/product/list'] );});}私人 loadCategories(){const url=SERVER_URL+CATEGORY_API_URL+'list';this.categoryService.getCategories( url ).subscribe(类别 =>{//@ts-忽略this.categories=categories;console.log( '成功加载类别' );},错误 1 =>{console.log('加载分类失败');},() =>{});}私人 loadCurrencies(){const url=SERVER_URL+CURRENCY_API_URL+'list';this.productService.getCurrencies( url ).subscribe(货币 =>{this.currencies=货币;},错误 1 =>{console.log('加载货币失败');},() =>{});}私有负载制造商(){const url=SERVER_URL+MANUFACTURER_API_URL+'list';this.productService.getManufacturers( url ).subscribe(制造商 =>{this.manufacturers=制造商;console.log('成功加载制造商');},错误 1 =>{console.log('加载制造商失败');},() =>{});}}
Angular 版本
Angular CLI:7.3.8节点:10.15.0操作系统:达尔文 x64角度:7.2.12... 动画、通用、编译器、编译器-cli、核心、表单... http、语言服务、平台浏览器... 平台浏览器动态,路由器套餐版本-----------------------------------------------------------@angular-devkit/架构师 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/核心 7.3.8@angular-devkit/schematics 7.3.8@angular/cli 7.3.8@ngtools/webpack 7.3.8@原理图/角度 7.3.8@原理图/更新 0.13.8rxjs 6.4.0打字稿 3.2.4网络包 4.29.0
解决方案
据我所知,您已经发出 HTTP 请求以从您的服务器获取数据,但您没有填充您的 productForm代码> FormGroup 正确的方式.由于您使用的是响应式表单,我强烈建议您使用 patchValue 或 setValue 以更新您的 FormControls.
对于您的情况,我会推荐 patchValue
,因为它比 setValue
更灵活.patchValue
不需要在参数中指定所有 FormControl 以更新/设置表单控件的值.
这是如何使用 patchValue 的.在您的 getProduct()
方法中,您可以通过执行此操作将 getProductDetails()
的 data
响应中的属性传递到您的 FormGroup 中;>
getProduct() {const id = this.route.snapshot.paramMap.get('id');const url = SERVER_URL + PRODUCT_API_URL + 'find/' + id;this.productService.getProductDetails(url).pipe().订阅(数据 =>{this.productForm.patchValue({id:数据.id名称:data.name描述:data.description//其他表单域})},错误 =>{控制台日志(错误);},() =>console.log('getProduct() 成功'));}
此外,在您的模板 html 上,无需在每个
或
上绑定您的 value
属性代码>.您可以删除所有这些.这是因为,您已经在使用 patchValue
更新值.
<label for="name">Name</label><input class="form-control" formControlName="name" id="name" type="text">
<div class="form-group"><label for="description">Description</label><input class="form-control" formControlName="description" height="100" id="description" required type="text" width="200">
当您需要从 productForm
获取数据时,您可以使用在 FormGroup 和 FormControls 上公开的 value
属性.
updateProduct() {const id = this.route.snapshot.paramMap.get('id');const url = SERVER_URL + PRODUCT_API_URL + '更新';//console.log(this.productFrom.value)const product = this.productForm.valuethis.productService.updateProduct(url, product).subscribe(值 =>{console.log('产品更新成功');}, error1 =>{console.log('更新产品失败');},() =>{this.router.navigate(['/product/list']);});}
In my use case when the user clicks on the edit button, Angular makes an HTTP call to the backend and retrieves the object, then populates those values on EDIT form. The user can update or leave the fields untouched. When clicked update
button, Angular should take all those values present in form and send them to the backend. So, here is the problem, after loading the values into edit page form and updating some fields and leaving some fields untouched makes untouched values empty. This is really strange
product-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 Versions
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
解决方案
As far as I can see, you have made the HTTP request to get the data from your servers, but you did not populate your productForm
FormGroup the right way. Since you are using reactive forms, I would highly recommend you to use patchValue or setValue to update your FormControls.
For your case, I would recommend patchValue
, as it is more flexible than setValue
. patchValue
do not require all FormControls to be specified within the parameters in order to update/set the value of your Form Controls.
This is how you can use patchValue. On your getProduct()
method, you can pass the properties in your data
response from getProductDetails()
into your FormGroup by doing this;
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'));
}
In addition, on your template html, there is no need to bind your value
attributes on each <input>
or <select>
. You can remove all of them. This is because, you are already updating the values using 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>
When you need to get data from your productForm
, you can use the value
property which is exposed on your FormGroup and 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']);
});
}
这篇关于当字段未触及时,Angular 反应形式返回空值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
查看全文