1
votes

Hi am trying to display a number of ingredient details in my Angular 2 Recipe app. I am using a FormArray, but when I run my and use the browser development tools to debug my code I can see the following error on the Console tab. I wonder if somebody could please explain to me what the problem is and what I need to do to rectify it:-

RecipeIngredientsDetailsComponent.html:17 ERROR Error: Cannot find control with path: 's_ingredientDetails -> ingredient_name'
    at _throwError (forms.js:2385)
    at setUpControl (forms.js:2255)
    at FormGroupDirective.addControl (forms.js:6606)
    at FormControlName._setUpControl (forms.js:7256)
    at FormControlName.ngOnChanges (forms.js:7169)
    at checkAndUpdateDirectiveInline (core.js:12092)
    at checkAndUpdateNodeInline (core.js:13598)
    at checkAndUpdateNode (core.js:13541)
    at debugCheckAndUpdateNode (core.js:14413)
    at debugCheckDirectivesFn (core.js:14354)

Below is my type script code:-

import { Component, Input, Output, EventEmitter, OnInit, ElementRef } from '@angular/core';
import { FormArray, FormGroup, FormControl, Validators, FormBuilder } from '@angular/forms';
import { Observable } from 'rxjs/Observable';

import { Ingredient } from '../ingredient';
import { IngredientDetailService } from '../ingredient-detail.service';


@Component({
  selector: 'app-recipe-ingredients-details',
  templateUrl: './recipe-ingredients-details.component.html',
  styleUrls: ['./recipe-ingredients-details.component.css'],
  providers: [IngredientDetailService]
})

export class RecipeIngredientsDetailsComponent implements OnInit {

  create_recipe_ingredient_detail_form: FormGroup;
  ingredients_arr: Ingredient[] = [];

  // @Output will tell the parent component (AppComponent) that an event happened in this component
  @Output() show_read_recipes_event = new EventEmitter();

  constructor(
    private formBuilder: FormBuilder,
    private ingredientDetailService: IngredientDetailService

  ) {  }

  @Input()
  r_id: number;

  @Input()
  s_ingredients: Ingredient[];

  ngOnInit() {
    this.create_recipe_ingredient_detail_form = this.formBuilder.group({
      s_ingredientDetails: this.formBuilder.array(
        this.s_ingredients.map(x => this.formBuilder.group({
          ingredient_name: [x.ingredient_name, [Validators.required]],
          ingredient_quantity: ['', [Validators.required]],
          ingredient_comment: ['', [Validators.required]],
        }))
      )
    })
  }

  createRecipeIngredientDetails(): void {
    this.ingredientDetailService.createRecipeIngredientDetails(this.create_recipe_ingredient_detail_form.value)
      .subscribe(
        ingredientDetail => {
          console.log(ingredientDetail);

          this.show_read_recipes_event.emit(
            { title: "Recipe Ingredients details" }
          );
        },
        error => console.log(error)
      );
  }

  dude(){
    console.log(this.s_ingredients);
  }

}

Below is my html:-

<div class="row">
    <div class="col-md-12">
      <form [formGroup]="create_recipe_ingredient_detail_form" (ngSubmit)="createRecipeIngredientDetails()">
        <table class="table table-hover table-responsive table-bordered">
          <tr>
            <th>
              Ingredient Name
            </th>
            <th>
              Quantity
            </th>
            <th>
              Comment
            </th>
          </tr>
          <tr formArrayName="s_ingredientDetails" *ngFor="let ingredient of create_recipe_ingredient_detail_form.get('s_ingredientDetails').controls; let i=index">
              <td>
                <div class="form-group">
                  <input name="ingredient_name" formControlName="ingredient_name" class="form-control" id="ingredient.ingredient_id" readonly />           
                </div>
              </td>
              <td>
                <div class="form-group">   
                  <input type="text" name="ingredient_qty" formControlName="ingredient_quantity" class="form-control" id="{{ingredient.ingredient_name}}" />
                </div>
              </td>
              <td>
                <div class="form-group">   
                  <input type="text" name="ingredient_cmt" formControlName="ingredient_comment" class="form-control" id="{{ingredient.ingredient_name}}" />
                </div>
              </td>
          </tr>
          <tr>
            <td>
              <input type="hidden" id="hidden_recipe_id" name="hidden_recipe_id" value="{{r_id}}" />             
            </td>
            <td></td>
            <td>
              <button type="submit" class="btn btn-primary">Create Recipe Ingredient Details</button>
            </td>
          </tr>
        </table>
      </form>
    </div>
  </div>

Below is a screen shot showing my form in runtime:-

enter image description here

2

2 Answers

2
votes

It it because your form array contains form groups, and they are keyed by the indices, you need to specify the group's name in the template so angular can bind your controls correctly to the group like this

<tr formArrayName="s_ingredientDetails" *ngFor="let ingredient of create_recipe_ingredient_detail_form.get('s_ingredientDetails').controls; let i=index">
    <!--
        Not semantically correct to have a div here, but it will solve the problem.
    -->
  <div [formGroupName]='i'>
    <td>
      <div class="form-group">
        <input name="ingredient_name" formControlName="ingredient_name" class="form-control" id="ingredient.ingredient_id" readonly />           
      </div>
    </td>
    <td>
      <div class="form-group">   
        <input type="text" name="ingredient_qty" formControlName="ingredient_quantity" class="form-control" id="{{ingredient.ingredient_name}}" />
      </div>
    </td>
    <td>
      <div class="form-group">   
        <input type="text" name="ingredient_cmt" formControlName="ingredient_comment" class="form-control" id="{{ingredient.ingredient_name}}" />
      </div>
    </td>
  </div>
</tr>

Also, it's 2018, stop using table to layout your content

0
votes

Thanks for help Dummy. Here is the working html:-

<tr formArrayName="s_ingredientDetails" *ngFor="let ingredient of create_recipe_ingredient_detail_form.get('s_ingredientDetails').controls; let i=index">
            <ng-container [formGroupName]='i'>
                <td>
                  <div class="form-group">
                    <input name="ingredient_name" formControlName="ingredient_name" class="form-control" id="ingredient.ingredient_id" readonly />           
                  </div>
                </td>
                <td>
                  <div class="form-group">   
                    <input type="text" name="ingredient_qty" formControlName="ingredient_quantity" class="form-control" id="{{ingredient.ingredient_name}}" />
                  </div>
                </td>
                <td>
                  <div class="form-group">   
                    <input type="text" name="ingredient_cmt" formControlName="ingredient_comment" class="form-control" id="{{ingredient.ingredient_name}}" />
                  </div>
                </td>
            </ng-container>
          </tr>