Is it possible to dynamically insert additional fields when a button is clicked?

My FormGroup is shown below:

this.productGroup = this.fb.group({
  name: ['', Validators.compose([Validators.required, Validators.maxLength(80)])],
  desc: ['', Validators.maxLength(3000)],
  category: ['', Validators.required]
  variants: this.fb.array([
    this.fb.group({
      type: '',
      options: ''
    })
  ])
});

I am looking to dynamically add control fields for type and options when the user clicks on a button. The FormArray should have items like this after user input:

[ {type: 'size', options: 'Small', 'Big'}, {type: 'color', options: 'red', 'blue, 'yellow'}, ... ]
.

This is what I'm trying to achieve:

// Function to add new item to FormArray
addItem(): void {
  this.variantsArray = this.productGroup.get('variants') as FormArray;
  this.variantsArray.push(this.fb.group({
    type: '',
    options: ''
  }));
}

// Template
<form [formGroup]="productGroup">
  // inputs...
  <div formArrayName="variants" *ngFor="let item of productGroup.controls['variants']; let i = index;">
      <div [formGroupName]="i">
        <div class="row">
          <mat-form-field class="col-12">
            <input formControlName="type">
          </mat-form-field>
        </div>
        <div class="row">
          <mat-form-field class="col-12">
            <input formControlName="options">
          </mat-form-field>
        </div>
      </div>
      <div class="row">
        <a href="javascript:" (click)="addItem()"> Add Variant </a>
        <a href="javascript:" (click)="removeItem(i)" *ngIf="i > 0"> Remove Variant </a>
      </div>
    </div>
</form>

How can I get it to work?

Answer №1

It seems like I may have an idea about what you're trying to achieve.

Here is the code snippet in question:

variants: this.fb.array([
    this.fb.group({
      type: '',
      options: ''
    })
  ])

Within this block of code, there is not actually an array created, so it cannot be looped through using *ngFor.

If you examine it closely, you will notice that

productGroup.controls['variants']

contains controls as a property.

To resolve this issue, simply update the *ngFor as follows:

*ngFor="let item of productGroup.controls['variants'].controls; let i = index;"

After making this adjustment, everything should function correctly.

Answer №2

To dynamically add a form element to a reactive form, start by creating the form element or group and then pushing it into the original form array for that field.

For example, consider a customerForm like this:

this.customerForm = this.fb.group({
  firstName: ['', [Validators.required, Validators.minLength(3)]],
  lastName: ['', [Validators.required, Validators.maxLength(50)]],
  emailGroup: this.fb.group({
    email: ['', [Validators.required, Validators.email]],
    confirmEmail: ['', Validators.required],
  }, { validator: emailMatcher }),
  phone: '',
  addresses: this.fb.array([this.createAddress()])
});

When the "Add Address" button is clicked in the view, a function in the component can be called to push a new address to the addresses array in the form.

addAddress(): void {
   this.addresses.push(this.createAddress());
}

createAddress(): FormGroup {
   return this.fb.group({
      street1: ['', Validators.required],
      street2: '',
      city: '',
      state: '',
      zip: ''
   });
}

In your view, iterate over the address array to display the addresses. Initially, they may not show as the array value will be empty. Hope this explanation helps.

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

Exploring the filter method in arrays to selectively print specific values of an object

const array = [ { value: "Value one", label: "Value at one" }, { value: "Value 2", label: "Value at 2" }, { value: "" , label: "Value at 3" } ...

Is real-time updating possible with data binding in Polymer and JavaScript?

I have a scenario where I am working with two pages: my-view1 and my-view2. On my-view1, there are two buttons that manipulate data stored in LocalStorage. On my-view2, there are two simple div elements that display the total value and the total value in t ...

Guide to incorporating JavaScript libraries from NPM into a child theme built on Understrap

I am looking to incorporate the Chart Js library within my custom Understrap Child Theme. Instead of simply using a CDN script, which could potentially slow down load times or cause errors if the CDN is not accessible, I have opted to import it into my pac ...

Creating a dynamic shift in background color

Is it possible to use jQuery to slowly change the color of diagonal lines in a background styled with CSS, while also adding a fading effect? I have created a fiddle with the necessary CSS to display a static background. You can view it here: http://jsfid ...

Passing JSON information through PatternLab

Incorporating an atomic pattern and passing data from a JSON array is my goal. Below are the code snippets and JSON file. anchor-link.mustache <a href="{{ url }}" class="{{ class }}">{{ label }}</a> footer-nav.mustache <ul class="menu ve ...

Exploring the depths of recursion with jQuery: Unraveling the

Having some issues with a recursive function in jQuery that's throwing an exception: 'Uncaught RangeError: Maximum call stack size exceeded' I can't figure out why this recursive function might be running infinitely. Any help would be ...

How to Position Logo in the Center of MUI AppBar in React

I've been struggling to center align the logo in the AppBar. I can't seem to get the logo to be centered both vertically and horizontally on its own. Here is my code from AppBar.tsx: import * as React from 'react'; import { styled, useT ...

Make sure to always target a specific DIV element, regardless of whether any of its child divs are

I have a div named "box" nested inside another div called "container." When I click on the box, I am able to retrieve its ID perfectly. However, when I click on the container, I want to obtain the ID of the box instead of the container. Is there a way fo ...

Implementing the 'not-allowed' cursor style on disabled rows in Material UI datagrid

I have a specific disabled row in a Material UI data grid where users are unable to select or perform any actions on it. I am looking to display the cursor as "not-allowed" on this particular row. How can we apply styling to only this row since there is no ...

What is the best method to dynamically navigate through JSON data and save an object at a specific level?

Here is an example json structure for a menu: { "menu": [{ "name": "vegetation", "id": "1", "children": [ { "name": "landuse", "id": "1.1", "children": [ ...

Ways to initiate SVG animations using Angular Component functions?

I am currently working on a project where I want to incorporate an animation that reflects the sorting process of an array of numbers. However, despite successfully sorting the numbers in the array, I am facing challenges with triggering the animations. I ...

Tips on incorporating a style-tag into the body of an Angular 5 application

We offer a service that collects a vast amount of data to be displayed in an angular 5 application. The model used for displaying this data is constantly evolving and shared across multiple applications, so I need to import it dynamically. My approach inv ...

Tips for setting a unique JWT secret in next-auth for production to prevent potential issues

Is there a way to properly set a JWT secret in NextAuth.js v4 to prevent errors in production? I have followed the guidelines outlined in the documentation, but I am still encountering this warning message without any further explanation: [next-auth][warn] ...

Choose or deselect images from a selection

I am currently working on a feature for an album creation tool where users can select photos from a pool of images and assign them to a specific folder. However, I'm facing difficulty in selecting individual photos and applying customized attributes t ...

Can anyone explain why the Splice function is removing the element at index 1 instead of index 0 as I specified?

selectedItems= [5,47] if(this.selectedItems.length > 1) { this.selectedItems= this.selectedItems.splice(0,1); } I am attempting to remove the element at index 0 which is 5 but unexpectedly it deletes the element at index ...

What can be done to prevent unnecessary API calls during re-rendering in a React application?

On my homepage, I have implemented code like this: {selectedTab===0 && <XList allItemList={some_list/>} {selectedTab===1 && <YList allItemList={some_list2/>} Within XList, the structure is as follows: {props.allItemList.map(ite ...

Steps for detecting a 401 Unauthorized error in SignalR when the token has expired

I have created a dynamic page that continuously fetches real-time information from my Azure functions backend using SignalR. If I am on the page for an hour and experience a disconnect, the signalr client will attempt to reconnect automatically, which usua ...

What is the best way to divide an array while extracting data from a JSON object using

Currently, I am parsing the json data. My goal is to find a specific property within the json object that contains two nested arrays (list and array). However, when extracting the values, they are all being stored in a single array. Is there a way to separ ...

Different TypeScript parameters that cannot be used together

Consider the given JavaScript function below: function x({foo, fooId, bar, barId}) {} I am looking to refactor this function into TypeScript in such a way that the caller is required to provide either foo or fooId, but not both. The same rule should apply ...

Error message: "jQuery is not defined and occurs exclusively in Chrome."

I've been using the following code to asynchronously load my JavaScript in the head section: <script type='text/javascript'> // Add a script element as a child of the body function downloadJSAtOnload() { var element4= document.creat ...