Discovering errors within a reactive form in Angular using a loop to iterate through the FormArray

I am currently utilizing Angular CLI version 16.2.1.

As I progress through a course, I am working with reactive forms. In a list of 'Recipes', I aim to include a Recipe with various inputs and a collection of ingredients. However, I am encountering errors every time I attempt to add an ingredient using a *ngFor loop of controls.

HTML

<div class="row">
    <div class="col-xs-12" formArrayName="ingredients">
        <div class="row" *ngFor="let ingredientCtrl of getControls(); let i = index" [formGroupName]="i" style="margin-top: 10px;">
            <div class="col-xs-8">
                <input type="text" class="form-control" formControlName="name">
            </div>
            <div class="col-xs-2">
                <input type="number" class="form-control" formControlName="amount">
            </div>
            <div class="col-xs-2">
                <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" (click)="onAddIngredient()">Add Ingredient</button>
            </div>
        </div>
    </div>
</div>

TypeScript (only code, no imports)

private initForm() {
    let recipeName = '';
    let recipeImagePath = '';
    let recipeDescription = '';
    let recipeIngredients = new FormArray([]);

    if(this.editMode) {
        const recipe = this.recipeService.getRecipe(this.id);
        recipeName = recipe.name;
        recipeImagePath = recipe.imagePath;
        recipeDescription = recipe.description;
        if(recipe['ingredients']) {
            for (const ingredient of recipe['ingredients']) {
                recipeIngredients.push(new FormGroup({
                    'name': new FormControl(ingredient.name, Validators.required),
                    'amount': new FormControl(ingredient.amount, [Validators.required, Validators.pattern(/^[1-9]+[0-9]*$/)])
                }));
            }
        }
    }

    this.recipeForm = new FormGroup({
        'name': new FormControl(recipeName, Validators.required),
        'imagePath': new FormControl(recipeImagePath, Validators.required),
        'description': new FormControl(recipeDescription, Validators.required),
        'ingredients': recipeIngredients
    })
}

getControls() {
    return (<FormArray>this.recipeForm.get('ingredients'))?.controls;
}

onAddIngredient() {
    (<FormArray>this.recipeForm.get('ingredients'))?.push({
        'name': new FormControl(null, Validators.required),
        'amount': new FormControl(null, [Validators.required, Validators.pattern(/^[1-9]+[0-9]*$/)])
    })
}

View and errors

https://i.sstatic.net/wNLYZ.png

Do you have any suggestions on how to resolve this issue?

Answer №1

When working with the onAddIngredient method, it is important to note that the FormGroup instance should be added to the FormArray instead of adding an object directly.

onAddIngredient() {
  let fg: FormGroup<any> = new FormGroup({
    name: new FormControl(null, Validators.required),
    amount: new FormControl(null, [
      Validators.required,
      Validators.pattern(/^[1-9]+[0-9]*$/),
    ]),
  });

  (<FormArray>this.recipeForm.get('ingredients'))?.push(fg);
}

Check out the demo on StackBlitz

Answer №2

Avoid using the function getControls(), instead, utilize a getter method

get ingredientsFormArray()
{
    return (<FormArray>this.recipeForm.get('ingredients'))
}

and loop through

*ngFor="let ingredientCtrl of ingredientsFormArray.controls";

Furthermore, I recommend creating two separate functions:

  1. Function that retrieves a formGroup for ingredients

    ingredientFormGroup(data:any=null)
    {
       data=data || {name:'',amount:''}
       return new FormGroup({
        name: new FormControl(data.name, Validators.required),
        amount: new FormControl(data.amount, [
          Validators.required,
          Validators.pattern(/^[1-9]+[0-9]*$/),
        ]),
      });
    }
    
  2. And a function that returns your formGroup

    formGroup(data:any=null) {
      data=data || {name:'',imagePath:'',descriptions:'',ingredients:[]}
      return new FormGroup({
            'name': new FormControl(data.name, Validators.required),
            'imagePath': new FormControl(data.imagePath, Validators.required),
            'description': new FormControl(data.description, Validators.required),
            'ingredients': new FormArray(data.ingredients
                   .map((x:any)=>this.ingredientFormGroup(x))
                   )
        })
    }
    

This allows you to write

if(this.editMode) {
    const recipe = this.recipeService.getRecipe(this.id);
    this.recipeForm=this.formGroup(recipe)
}
else
    this.recipeForm=this.formGroup()

And

//to add a new ingredient
this.ingredientsFormArray.push(this.ingredientFormGroup())

NOTE: It is possible that the previous code did not work because your this.recipeService.getRecipe() should return an observable, not an object (remember: "services return observables, we subscribe in components" and the correct way is

this.recipeService.getRecipe(this.id).subscribe((recipe:any)=>{
    this.recipeForm=this.formGroup(recipe)
 });

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Creating an interceptor to customize default repository methods in loopback4

Whenever I attempt to access the default repository code, I need to manipulate certain values before triggering the default crud function in the repository. How can I accomplish this? For example: ... @repository.getter('PersonRepository') priva ...

The error message "Webpack is not applying the style from leaflet.css"

Currently utilizing the ngX-Rocket angular 8 starter, I am looking to integrate the leaflet map library into my project from this source: https://github.com/Asymmetrik/ngx-leaflet After including the file in my index.html document, I encountered the follo ...

Angular Universal experiences issues with route functionality after page reloads

Recently deployed an Angular Universal web app on Firebase. While all routes within the application are functioning properly, encountering errors when trying to access them externally. Below is the error message: functions: Starting execution of "ssr" ⚠ ...

What is the proper way to access and modify the child component within a parent component?

I am working with nested components as shown below: app.parent.component > app.container.component > app.containeritem.component Here is an example: app.parent.component import ... @Component({ selector:'parent', template: &apos ...

Can the string literal type be implemented in an object interface?

My custom type is called Color, type Color = 'yellow' | 'red' | 'orange' In addition, I created an object with the interface named ColorSetting. interface ColorSetting { id: string yellow?: boolean red?: boolean orang ...

The Vue application combined with TypeScript is displaying an empty white screen

I've enrolled in a Vue + Firestore course, but I'm attempting to use TypeScript instead of conventional JavaScript. The basic setup is complete, however, my app displays a blank page when it should be showing a simple header text from the App.vue ...

Using Angular to dynamically modify the names of class members

I am working with an Angular typescript file and I have a constant defined as follows: const baseMaps = { Map: "test 1", Satellite: "test 2" }; Now, I want to set the member names "Map" and "Satellite" dynam ...

Importing the Ivy library component in Angular by specifying the module path as a string

Currently, I'm working with the latest Ivy release candidate. Scenario: I need to make multiple modules available for separate compilation and runtime loading using import statements, without relying on loadChildren from the router module. Backgroun ...

Debugging TypeScript code in VS Code by stepping into a library function within the Node.js debugger

After developing a TypeScript library and its corresponding testing program, I have implemented source maps for both projects. Utilizing the node.js debugger, I am now faced with an issue. While debugging my TypeScript code in the program is successful, t ...

Refresh a Google chart without having to reload the entire page

I currently have a button that allows me to refresh the data on my page in case there is new data available through an API. Although this button successfully refreshes my datatable, it does not redraw the Google charts I have integrated into my project usi ...

Issue: [ts] Unable to locate the term 'React'

Due to specific requirements, I have made some customizations to the Ionic component: range. I changed the class name of the component from Range to CustomRange (with selector: custom-range): https://github.com/ionic-team/ionic/blob/master/core/src/compon ...

When trying to set the focus on the first item in a list using HTML and Angular, the focus unexpectedly shifts to the second

I've been tackling a UI requirement where the focus needs to be set on the first element of a list item constructed from an array of objects when the tab key is pressed for the first time. Subsequent tab key presses should cycle through the list items ...

Develop a type definition utilizing dotted paths from a recursive object model

Working with TypeScript, I am dealing with a nested object structure of functions defined as: type CallbackFn = (args: any) => any type CallbackObj = { [key: string]: CallbackFn | CallbackObj } const callbacks = { foo: function(args: { x: num }): st ...

Using TypeScript to Manipulate SVG Polyline Shapes

In my TypeScript 3.1.1 project in VS Code with an Aurelia setup, I am facing a challenge while trying to manipulate an SVG Polyline using TypeScript code. The issue arises when attempting to create a new SVGPoint object. The initial HTML structure is as fo ...

Javascript Library Issue: "Implicitly Declared Type 'Any' Error"

I am currently in the process of developing a JavaScript library that will interact with an API. My goal is to create a module that can be easily published on npm and utilized across various frameworks such as Angular or React. Below is the code snippet fo ...

Include a link in the email body without displaying the URL

My concern revolves around displaying a shortened text instead of the full link within an email. When the recipient clicks on "Open Link," they should be redirected to the URL specified. The text "Open Link" must appear in bold. My current development fram ...

Retrieving the status of a checkbox using Angular's field binding feature

While using Angular 5.1.1, I am facing an issue where a function is not called correctly when a checkbox changes. This problem seems to occur specifically with the checkbox input type, as it always sends the value "on" to the function even when the checkbo ...

Mapping fields in Angular collectively

I'm currently working on implementing a modal, and I'm looking to link values from the formBuilder to a specific property. Here's the snippet of code I'm working with: submit(data?: any) { // THE FOLLOWING CODE WORKS, BUT IT'S ...

Retrieve request body/header variables in JWT strategy using Nest JS

Is there a way to retrieve the header variables or request body in a strategy? The JWT structure within the JwtStrategy currently appears as follows: @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { constructor( private re ...

How to Use ngFor to Create a Link for the Last Item in an Array in Angular 7

I need help with adding a link to the last item in my menu items array. Currently, the menu items are generated from a component, but I'm unsure how to make the last item in the array a clickable link. ActionMenuItem.component.html <div *ngIf= ...