Реактивный FormsModule в Angular2
Я пытался добавить FormsArray в мой проект при использовании ReactiveFormsModule, но я получаю сообщение об ошибке, и оно отображается как:-
Не удается найти элемент управления с неопределенным атрибутом имени. а также мы не можем добавить FormsArray с помощью формы на основе шаблона?
Ниже мой код для этого.
Рецепт-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>
Рецепт-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
})
}
}
2 ответа
Я полностью согласен с рекомендациями, сделанными DeborahK в предыдущем ответе, и вы должны следовать им при использовании реактивных форм.
Тем не менее, это не причина вашей ошибки. В вашем 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>
Как видите, я обернул имя и сумму контроля в div
и переместил [formGroupName]
к этой обертке Я не тестировал код, но он должен решить вашу проблему.
Человек, написавший угловые формы, настоятельно рекомендуется не смешивать управляемые шаблоном и реактивные формы. 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 || []));
}
Дайте мне знать, если у вас есть какие-либо вопросы по любому из примеров.