Angular2中的反应式FormsModule

时间:2019-02-04 16:29:20

标签: angular

我尝试使用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
})
}

 }

2 个答案:

答案 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中。我没有测试代码,但是它可以解决您的问题。