struggling to implement dynamic reactive forms with Angular

Currently, I am experimenting with building a dynamic reactive form using Angular. While I have successfully implemented the looping functionality, I am facing some challenges in displaying it the way I want.

<form [formGroup]="registerForm" (ngSubmit)="onSubmit()">
  <div *ngFor="let dat of formData; let index = index;">
     <label for="{{dat.name}}">{{dat.name }}</label>
     <input type="text" id="{{dat.id}}" [formControlName]="dat.name" />  
    </div>


</form>
<button type="button"><button>

.ts

 registerForm: FormGroup;
  submitted = false;

  formData = [{ id: 'firstName', name: 'firstName' },
  { id: 'lastName', name: 'lastName' },
  { id: 'address', name: 'address' },
  { id: 'emailid', name: 'emailid' }
  ]


  constructor(private formBuilder: FormBuilder) {
    this.registerForm = this.formBuilder.group({
      formData: []
    });
   }

   onSubmit(){
    console.log(this.registerForm.value); 
   }

Moreover, I am looking to add validation to the form as well.

You can check out my code on StackBlitz by following this URL:

https://stackblitz.com/edit/angular-awguej

Answer №1

One way to achieve this is by using the this.formBuilder.array method in Angular.

Check out this Stackblitz example for reference.

component.html

<form [formGroup]="registerForm" (ngSubmit)="onSubmit()">
    <div formArrayName="formData">
        <div *ngFor="let dat of registerForm.get('formData').controls;let index = index;" [formGroupName]="index">
            <label>{{dat.get('name').value }}</label>
     <input type="text" formControlName="name" />  
    </div>
<button type="submit">Submit</button>
  </div>
</form>
<button type="button" (click)="addNew()">Add</button>

component.ts

registerForm: FormGroup;
  submitted = false;

  constructor(private formBuilder: FormBuilder) {
    this.registerForm = this.formBuilder.group({
      formData: this.formBuilder.array([this.createNewGroup()])
    });
   }

   createNewGroup() {
    return this.formBuilder.group({
      id: ['id'], 
      name: ['name'] 
    })
  }

  addNew(){
    let formArr=this.registerForm.get('formData') as FormArray;
    formArr.push(this.createNewGroup());
  }

   onSubmit(){
    console.log(this.registerForm.value); 
   }

Answer №2

To ensure proper control over an input field, it is essential to include an ID for control purposes, a name for labeling, and a value for setting the input value. This approach will prove beneficial when implementing a more dynamic solution.

Within your component, you can follow this structure:

formData = [{ id: 'firstName', name: 'firstName', value: '' },
  { id: 'lastName', name: 'lastName', value: '' },
  { id: 'address', name: 'address', value: '' },
  { id: 'emailid', name: 'emailid', value: '' }
  ]

constructor(private formBuilder: FormBuilder) {
    let formGroups: FormGroup[] = []; 
    for(let i = 0; i < this.formData.length; i++){
      formGroups.push(this.formBuilder.group({
          id: this.formData[i].id,
          name: this.formData[i].name,
          value: this.formData[i].value,
      }));
    }
    this.registerForm = this.formBuilder.group({
        array: this.formBuilder.array(formGroups)
    })
   }

   onSubmit(){
    console.log(this.registerForm.value); 
   }

In your HTML template, include the following:

<form [formGroup]="registerForm" (ngSubmit)="onSubmit()">
  <div formArrayName="array">
    <div *ngFor="let dat of registerForm.get('array').controls;let i = index;" [formGroupName]="i">
        <label>{{dat.get('name').value}} </label>
        <input type="string" id={{dat.value.id}} #input formControlName="value">
    </div>
  </div>
</form>

<button><button>

Answer №3

You have the option to include controls in this manner.

component.ts

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, FormArray, FormControl, ValidatorFn } from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

  registerForm: FormGroup;
  submitted = false;

  formData = [{ id: 'firstName', name: 'firstName' },
  { id: 'lastName', name: 'lastName' },
  { id: 'address', name: 'address' },
  { id: 'emailid', name: 'emailid' }
  ]


  constructor(private formBuilder: FormBuilder) {
    this.registerForm = this.formBuilder.group({
      list: this.formBuilder.array([])
    });
  }

  ngOnInit() {
    this.registerForm = this.formBuilder.group({
      list: this.formBuilder.array(
        this.formData.map(x => this.formBuilder.group({
          name: [x['name']],
          id: [x['id']]
        }))
      )
    })
  }

  onSubmit() {
    console.log(this.registerForm.value);
  }
}

Component.html

<form [formGroup]="registerForm" (ngSubmit)="onSubmit()">
  <span formArrayName="list">
  <div *ngFor="let dat of registerForm.get('list').controls;let index = index;" formGroupName="{{index}}">
     <label for="{{dat.name}}">{{dat.name }}</label>
     <input type="text" id="{{dat.get('id').value}}" formControlName="name" />  
    </div>
  </span>
<button type="submit">button</button>
</form>

Answer №4

To add to Krishna's response, if you're looking to generate a formArray with data input, you can utilize two specific functions.

// Function to construct a formGroup with provided data or empty if there is none
createNewGroup(data): FormGroup {
    return this.formBuilder.group({
        id: [data ? data.id : null], 
        name: [data ? data.name : null] 
    })
}

// Function for creating the actual form

createForm(formData): FormGroup {
    // Generate an array of FormGroups,
    // If there is data, create a new group for each item in the data array
    // Otherwise, create an array with one element (null)
    let group = formData ? formData.map(d => this.createNewGroup(d)) 
                        : [this.createNewGroup(null)]

    return this.formBuilder.group({
         formData: this.formBuilder.array(group)
       });
}

Therefore, you have the flexibility to:

this.registerForm = createForm(this.formData) // Generate a form with existing data
this.registerForm = createForm(null) // Generate an empty form

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

Explore the features of Angular4 including the router and optional children parameters

I am interested in creating paths like these: matches/:page/:team/:season with the flexibility of having :team and :season as optional parameters. For example, I would like to be able to use URLs such as: matches/results/4/2017 or matches/results/4 or ...

What is the best method for extracting the selected value from a Dropdown in ng-bootstrap?

I am currently utilizing ng-bootstrap and I am interested in obtaining the selected value from a dropdown. <div class="col text-right"> <div ngbDropdown placement="top-right" class="d-inline-block"> <button class="btn btn-outline-primary ...

Concealing forms within an Angular 5 application

I'm currently working on displaying the terms of use on the initial screen along with two buttons. If the user clicks the accept button, they will be directed to the authentication form. However, if they click refuse, the "Refused Terms" screen will a ...

The favicon refuses to load

Image 'http://localhost:8000/favicon.ico' could not be loaded as it contravened the Content Security Policy directive "default-src 'none'". It is important to note that since 'img-src' was not specifically defined, 'defau ...

When the @Input() contains an unaltered boolean value, it does not initiate change detection

I have developed an Angular application featuring a popup component. The visibility of the popups can be controlled both from its parent and the popup itself. app.component.ts import { Component } from '@angular/core'; @Component({ selector: ...

Validating dates in TypeScript

Currently, I am studying date handling and have an object that contains both a start and end date. For example: Startdate = "2019-12-05" and Enddate = "2020-05-20" My goal is to establish a condition that first verifies the dates are not empty. After tha ...

Tips for refreshing the current page in Angular without being redirected to the login page

Exploring an Angular example for login and registration here on stackblitz Encountering an issue where after refreshing the page, the authguard redirects me to the login page even though I am already logged in. Looking for a solution to redirect to the c ...

Getting the Value of a CSS Class through Form Name in Angular

I am attempting to retrieve the class name of a form control using the Form Name both in HTML and in the Class file The code snippet below functions correctly and shows the value of the control {{ModelForm.value.firstName| json}} However, when I try to d ...

What is the most efficient method for integrating third-party components just once in an Angular 8 application?

Is there a preferred method for configuring 3rd party components? I frequently use primeng p-table in various components throughout my application. I am looking to configure it globally for the entire application - enabling the paginator by default, setti ...

Typescript: creating index signatures for class properties

Encountering a problem with index signatures while attempting to access static and instantiated class properties dynamically. Despite researching solutions online, I have been unable to resolve the issue. The problem was replicated on a simple class: int ...

What could be causing ng-show to fail to hide a div and its content on my page?

Hello everyone, I am brand new to Angular and I have encountered an issue while trying to hide a div and its content within a view. I attempted to use the NgShow directive as outlined in this helpful tutorial: https://scotch.io/tutorials/how-to-use-ngshow- ...

Choose a specific location on a vehicle illustration within a pop-up window. The image should be partitioned into 6 separate sections

I have a project for my client where they need to choose a car and then indicate where the damage is located on an image, which is divided into 6 sections. I'm struggling to figure out how to achieve this. I initially thought z-index would work, but ...

Dealing with a sequence of deletions in Angular 4

When working with a ReplaySubject of size 3 and a delete chain that must be in order, I encountered an issue. After the deletion process is completed, I need to redirect the user. However, when subscribing to this ReplaySubject method, it sends "here fin ...

Angular expands the HTML of the parent component

Although this question may be old, I am still struggling to find a straightforward answer and it just doesn't make sense to me. I have a main component with its own HTML and several components that inherit from it. Here is what I am trying to achiev ...

Accessing dynamic data beyond the subscribe method function

Having some trouble creating a function that retrieves an object from a service using an external API. I'm struggling to get it functioning properly. FetchMatchInfo (matchId : number): Match { let retrievedMatch: Match; this.matchService.Ge ...

Why are my variables resetting in Angular after ngAfterViewInit?

There seems to be an issue with my variables resetting after successfully using them in ngAfterViewInit(). I have a few @ViewChild and regular variables that are utilized or set in ngAfterViewInit. However, when certain events that I added post-initializa ...

Creating custom observables by utilizing ViewChildren event and void functions in Angular 12: A step-by-step guide

I am currently working on developing a typeahead feature that triggers a service call on keyup event as the user types in an input field. The challenge I face is that my input field is enclosed within an *ngIf block, which requires me to utilize ViewChildr ...

Typescript: The type 'Observable<{}>' cannot be assigned to the type 'Observable'

I'm encountering an issue with the Observable type, any thoughts on how to resolve it? import { PostModel } from '../model/postModel'; import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Observable&ap ...

Setting up a passthrough for a directory of external resources in Angular

Issue: I am currently facing a challenge with a project that requires accessing photos from a system that has recently been updated from AngularJs to Angular 16. Initially, we were able to serve these photos from a virtual directory like https://myawesom ...

Executing the function in Ionic 4 when the events are absent

In my Ionic 4 multilingual app, I am fetching data from an API based on the selected language. I have set up an event for this purpose, but I face an issue when the event value does not exist - in such cases, I want to run a default function. Below is the ...