1

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

2

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

Sign up to request clarification or add additional context in comments.

2 Comments

I would suggest you using ng-container instead of div here. Then it should be okay semantically)
Yes it now works brilliant. Thank you very much for your help.
0

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>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.