简体   繁体   中英

Reactive FormsModule in Angular2

I tried to Add FormsArray in my Project while using ReactiveFormsModule but i am receiving the error and it is displayed as :-

Cannot find control with unspecified name attribute . and also can't we add FormsArray by using template driven form ?

Below is my code for this .

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
})
}

 }

It is highly recommended by the person who wrote Angular forms to not mix Template-driven and Reactive forms. FormArray is part of Reactive forms.

There are a few 'template-driven' form techniques in your code that you should consider removing, such as this:

@ViewChild('f') recipeform:FormGroup

You are defining the recipeform FormGroup in your code, then generating a reference to it in your template, then passing that reference back to your code. (I'm surprised that this does not cause an error.)

I would recommend:

1) Removing #f="ngForm" from your template. That is only needed for template-driven forms.

2) Replacing @ViewChild('f') recipeform:FormGroup with just a declaration for recipeform: FormGroup .

3) Using FormBuilder instead of instances of FormGroup and FormControl.

4) Is this an async call: const recipe=this.reservice.getrecipe(this.id) , if so, you need to use subscribe to get the data.

Here is an example of one of my reactive forms using FormBuilder and a 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: ''
        });
      }
}

You can find a complete working example here: https://github.com/DeborahK/Angular-ReactiveForms

I have a second example in the same repo that includes the parameter code similar to what you are doing:

  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 || []));
  }

Let me know if you have any questions about either example.

I totally agree with the recommendations made by @DeborahK in the previous answer and you should follow those while using Reactive forms.

However, those are not the reason for your error. In your HTML template, you have a wrong hierarchy of your FormArray control. It should be FormArray --> FormGroup --> FormControl, like this:

<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>

As you can see, I have wrapped the name and amount control within a div and moved the [formGroupName] to this wrapper div. I didn't test the code but it should solve your problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM