我尝试使用ReactiveFormsModule时在我的项目中添加FormsArray,但我收到错误,并且显示为:-
找不到具有未指定名称属性的控件。 我们也不能通过使用模板驱动的表单添加FormsArray吗?
下面是我的代码。
recipe-edit.component.ts
<div class="row">
<div class="col-xs-12">
<form [formGroup]="recipeform" (ngSubmit)="onsubmit()" #f="ngForm">
<div class="row">
<div class="col-xs-12">
<button
type="submit"
class="btn btn-success"
>Save</button>
<button type="button" class="btn btn-danger" >Cancel</button>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label for="name">Name</label>
<input
type="text"
id="name"
formControlName="name"
class="form-control">
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label for="imagePath">Image URL</label>
<input
type="text"
id="imagePath"
formControlName="image"
class="form-control"
>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12" >
<img class="img-responsive">
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label for="description">Description</label>
<textarea
type="text"
id="description"
class="form-control"
formControlName="description"
rows="6"></textarea>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12" >
<div
class="row"
formArrayName="ingredients"
*ngFor="let ctrl of recipeform.get('ingredients').controls;let i=index"
[formGroupName]="i"
style="margin-top: 10px;">
<div class="col-xs-8">
<input
type="text"
formControlName="name"
class="form-control"
>
</div>
<div class="col-xs-2">
<input
type="number"
class="form-control"
formControlName="amount"
>
</div>
<div class="col-xs-2">
<button
type="button"
class="btn btn-danger"
>X</button>
</div>
</div>
<hr>
<div class="row">
<div class="col-xs-12">
<button
type="button"
class="btn btn-success"
>Add Ingredient</button>
</div>
</div>
</div>
</div>
</form>
recipe-edit.component.ts
import { Component, OnInit, ViewChild } from '@angular/core';
import {NgForm, FormGroup, FormControl, FormArray} from
'@angular/forms';
import { Recipeservice } from '../recipe.service';
import { ActivatedRoute, Router } from '@angular/router';
@Component({
selector: 'app-recipe-edit',
templateUrl: './recipe-edit.component.html',
styleUrls: ['./recipe-edit.component.css']
})
export class RecipeEditComponent implements OnInit {
@ViewChild('f') recipeform:FormGroup
id:number
editmode=false
constructor(private reservice:Recipeservice,private
route:ActivatedRoute,router:Router) { }
ngOnInit() {
this.route.params.subscribe(
(params)=>{
this.id=+params['id']
console.log(this.id)
this.initform()
}
)
}
onsubmit(){
console.log(this.recipeform)
}
private initform(){
let recipename=''
let recipeimage=''
let recipedescription=''
let recipeingredients=new FormArray([])
this.editmode=true
if(this.editmode){
const recipe=this.reservice.getrecipe(this.id)
recipename=recipe.name
recipeimage=recipe.imagepath
recipedescription=recipe.description
if(recipe.ingredients!=null){
for(let ingredient of recipe.ingredients){
recipeingredients.push(new FormGroup({
'name':new FormControl(ingredient.name),
'amount':new FormControl(ingredient.amount)
}))
}
}
}
this.recipeform=new FormGroup({
'name':new FormControl(recipename),
'image':new FormControl(recipeimage),
'description':new FormControl(recipedescription),
'ingredients':recipeingredients
})
}
}
答案 0 :(得分:3)
强烈建议编写Angular表单的人不混合使用模板驱动和响应式表单。 FormArray是Reactive表单的一部分。
您的代码中应考虑删除一些“模板驱动”的表格技术,例如:
@ViewChild('f') recipeform:FormGroup
您要在代码中定义recipeform
FormGroup,然后在模板中生成对其的引用,然后将该引用传递回代码。 (我很惊讶这不会导致错误。)
我建议:
1)从模板中删除#f="ngForm"
。只有模板驱动的表单才需要。
2)仅用@ViewChild('f') recipeform:FormGroup
的声明代替recipeform: FormGroup
。
3)使用FormBuilder代替FormGroup和FormControl的实例。
4)这是一个异步调用:const recipe=this.reservice.getrecipe(this.id)
,如果是这样,则需要使用subscribe
来获取数据。
这是我使用FormBuilder和FormArray的一种反应形式的示例:
export class CustomerComponent implements OnInit {
customerForm: FormGroup;
ngOnInit() {
this.customerForm = this.fb.group({
firstName: ['', [Validators.required, Validators.minLength(3)]],
lastName: ['', [Validators.required, Validators.maxLength(50)]],
addresses: this.fb.array([this.buildAddress()])
});
}
addAddress(): void {
this.addresses.push(this.buildAddress());
}
buildAddress(): FormGroup {
return this.fb.group({
addressType: 'home',
street1: ['', Validators.required],
street2: '',
city: '',
state: '',
zip: ''
});
}
}
您可以在此处找到完整的工作示例:https://github.com/DeborahK/Angular-ReactiveForms
同一仓库中还有第二个示例,其中包括与您正在执行的操作类似的参数代码:
ngOnInit(): void {
this.productForm = this.fb.group({
productName: ['', [Validators.required,
Validators.minLength(3),
Validators.maxLength(50)]],
tags: this.fb.array([]),
description: ''
});
// Read the product Id from the route parameter
this.sub = this.route.paramMap.subscribe(
params => {
const id = +params.get('id');
this.getProduct(id);
}
);
}
getProduct(id: number): void {
this.productService.getProduct(id)
.subscribe(
(product: Product) => this.displayProduct(product),
(error: any) => this.errorMessage = <any>error
);
}
displayProduct(product: Product): void {
if (this.productForm) {
this.productForm.reset();
}
this.product = product;
// Update the data on the form
this.productForm.patchValue({
productName: this.product.productName,
description: this.product.description
});
this.productForm.setControl('tags', this.fb.array(this.product.tags || []));
}
如果您对这两个示例有任何疑问,请告诉我。
答案 1 :(得分:2)
我完全同意@DeborahK在上一个answer中的建议,在使用反应式表单时,您应该遵循这些建议。
但是,这些并不是您出错的原因。在HTML模板中,您的FormArray
控件层次结构错误。应该是FormArray-> FormGroup-> FormControl,像这样:
<div class="row"
formArrayName="ingredients"
*ngFor="let ctrl of recipeform.get('ingredients').controls;let i=index"
style="margin-top: 10px;">
<div [formGroupName]="i">
<div class="col-xs-8">
<input type="text"
formControlName="name"
class="form-control">
</div>
<div class="col-xs-2">
<input type="number"
class="form-control"
formControlName="amount">
</div>
<div class="col-xs-2">
<button type="button"
class="btn btn-danger">
X
</button>
</div>
</div>
</div>
如您所见,我已经将 name 和 amount 控件包装在div
中,并将[formGroupName]
移到了该包装div中。我没有测试代码,但是它可以解决您的问题。