0

I have succeeded in creating multiple forms based on the value from the dropdown list, but I am not able to save the data from the input element into database. If my dropdown list displays the value 5, then 5 forms with the same number of input element will be shown and be ready for data entry. My problem is how to save these data from one or multiple forms textbox into database.

Service.ts

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import {  Offspring } from './animal';

@Injectable({
  providedIn: 'root'
})
export class AnimalService {

  readonly Urloffspring = 'api/AnimalAPI'
  constructor(private http:HttpClient) { }

  formOffspring:Offspring = new Offspring();
  formSubmitted: boolean = false; 
  
  postOffspring(formOffspring: any){
    console.log(formOffspring);
    return this.http.post<any>(this.Urloffspring, formOffspring);
  }
}

**Component.ts

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder } from '@angular/forms';
import { AnimalService } from '../animal.service';
@Component({
  selector: 'app-fetch-data',
  templateUrl: './fetch-data.component.html',   
})

export class FetchDataComponent implements OnInit {
  reactForm: FormGroup;
  public forms: FormGroup[] = [];
  public unitData: Object[] = [
    { Id: 'Form1', Form: '1' },
    { Id: 'Form2', Form: '2' },
    { Id: 'Form3', Form: '3' },
    { Id: 'Form4', Form: '4' },
    { Id: 'Form5', Form: '5' },
  ];
  public val: string = "1";
  public unitFields: Object = { text: 'Form', value: 'Form' };
  public unitWaterMark: string = '';

  ngOnInit(): void {
    this.generateForms(1); 
  }

  generateForms(count: number): void {
    this.forms = [];
    for (let i = 0; i < count; i++) {
    this.forms.push(
    this.fb.group({
     Id: (0),
     Animal: (null),
     Tagno: (null),        
    })
   )}
  }

  dropdownChange(args: any): void {
    const selectedCount = parseInt(args.value, 10);
      if (!isNaN(selectedCount)) {
        this.generateForms(selectedCount);
    }
  }

  onSubmit(): void {
    if (this.forms.every(form => form.valid)) {
      const allFormData = this.forms.map(form => form.value);
  }
  
  // ... validation code ...
  this.service.postOffspring(this.forms).subscribe({
    next: (response) => {
      console.log('Forms saved successfully', response); 
    },
    error: (error) => {
      console.error('Error saving forms', error);
    }
    });
  }

  onSubmitAll() {
  let allValid = true;
    const allFormData = [];
    this.forms.forEach((form, index) => { 
      if (form.valid) {
        allFormData.push(form.value);
      } else {
        form.markAllAsTouched();
        allValid = false;
      }
    });

    if (allValid) {
    // here can you get the all forms data and send it to backend using the angular service.
    } 
   }
 }
 
  constructor(private fb: FormBuilder, private service: AnimalService ) {}
}

html

<div class="col-lg-12 control-section">
  <div class="content-wrapper" style="max-width: 600px; margin: 0 auto;">      
   <ejs-dropdownlist
   id="Unt"
   [dataSource]="unitData"
   type="text"
   name="Unt"
   [fields]="unitFields"
   placeholder="Mark All"
   (change)="dropdownChange($event)" 
   [query]="unitquery"
   floatLabelType="Auto"
   [value]="val"
 >
 </ejs-dropdownlist><br><br>

 <div *ngFor="let form of forms; let i = index">
   <form [id]="'formId' + i"  [formGroup]="form" (ngSubmit)="onSubmit(form, i)" class="form-horizontal" novalidate>
     <div class="form-title">
       <span>Customer {{ i + 1 }}</span>
     </div>
 
     <div class="form-group">
       <div class="e-float-input">
         <input [id]="'Id' + i" formControlName="Id" />
         <span class="e-float-line"></span>
         <label [for]="'Id' + i" class="e-float-text"></label>
       </div>           
     </div>
 
     <div class="form-group">
       <div class="e-float-input">
         <input [id]="'Animal' + i" type="text" formControlName="Animal" />
         <span class="e-float-line"></span>
         <label [for]="'Animal' + i" class="e-float-text">Animal</label>
       </div>
     </div>
 
     <div class="form-group">
       <div class="e-float-input">
         <input [id]="'Tagno' + i" type="text" formControlName="Tagno" />
         <span class="e-float-line"></span>
         <label [for]="'Tagno' + i" class="e-float-text">Tagno</label>
       </div>
     </div>

   </form>
 </div>    

Backend

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Project1.Models;
using System.Data.SqlClient;
using System.Data;
using Microsoft.Extensions.Configuration;

namespace Project1.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class AnimalAPIController : ControllerBase
    {
        private dbContext _context;
    
        public AnimalAPIController(dbContext context, IConfiguration configuration)
        {
            _context = context;
        }
   
        [HttpPost]
        public void Post([FromBody] Animaldb book)
        {
            _context.Animaldb.Add(book);
            _context.SaveChanges();
        }

    }
}

1 Answer 1

2

We should use the FormArray angular element, to initialize an array which contains a group of form group.

We can just loop through the controls and initialize the individual form groups of the form array.

The below is the main code we need to note:

<form
  [formGroup]="reactForm"
  (ngSubmit)="onSubmit()"
  class="form-horizontal"
  novalidate
>
  <div formArrayName="data">
    <div *ngFor="let form of formArray.controls; let i = index">
      <div [formGroupName]="i">
        <div class="form-title">
          ...

In the above code, we initialize a single form, the container with formArrayName will define the form array, that will contain the individual sub form groups.

Then we loop through the controls of the form array and initialize the group using [formGroupName]="i" this will update the individual form groups with the data filled.


When we generate the form using the method, we simply loop through the count and push the generated form controls inside the form array using push.

generateForms(count: string): void {
  this.reactForm = this.fb.group({
    data: new FormArray([]),
  });
  for (let i = 0; i < +count; i++) {
    this.formArray.push(
      this.fb.group({
        Id: new FormControl(0),
        Animal: new FormControl(null),
        Tagno: new FormControl(null),
      })
    );
  }
}

The submit method will simplified to this.reactForm.invalid to check the forms validity and this.reactForm.markAllAsTouched(); to mark the entire form as touched.

onSubmit(): void {
  if (this.reactForm.invalid) {
    this.reactForm.markAllAsTouched();
  }
  alert(JSON.stringify(this.reactForm.value, null, 4));
  // ... validation code ...
  this.service.postOffspring(this.reactForm.value).subscribe({
    next: (response) => {
      console.log('Forms saved successfully', response);
    },
    error: (error) => {
      console.error('Error saving forms', error);
    },
  });
}

Full Code:

import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import {
  ReactiveFormsModule,
  FormsModule,
  FormGroup,
  FormArray,
  FormControl,
  FormBuilder,
} from '@angular/forms';
import { Injectable } from '@angular/core';
import { HttpClient, provideHttpClient } from '@angular/common/http';
import { CommonModule } from '@angular/common';
export class Offspring {}

@Injectable({
  providedIn: 'root',
})
export class AnimalService {
  readonly Urloffspring = 'api/AnimalAPI';
  constructor(private http: HttpClient) {}

  formOffspring: Offspring = new Offspring();
  formSubmitted: boolean = false;

  postOffspring(formOffspring: any) {
    console.log(formOffspring);
    return this.http.post<any>(this.Urloffspring, formOffspring);
  }
}

@Component({
  selector: 'app-root',
  imports: [ReactiveFormsModule, FormsModule, CommonModule],
  templateUrl: './main.html',
})
export class App {
  reactForm!: FormGroup;
  public unitData: any[] = [
    { Id: 'Form1', Form: '1' },
    { Id: 'Form2', Form: '2' },
    { Id: 'Form3', Form: '3' },
    { Id: 'Form4', Form: '4' },
    { Id: 'Form5', Form: '5' },
  ];
  public val: string = '1';
  public unitFields: Object = { text: 'Form', value: 'Form' };
  public unitWaterMark: string = '';

  constructor(private fb: FormBuilder, private service: AnimalService) {}

  ngOnInit(): void {
    this.generateForms('1');
  }

  generateForms(count: string): void {
    this.reactForm = this.fb.group({
      data: new FormArray([]),
    });
    for (let i = 0; i < +count; i++) {
      this.formArray.push(
        this.fb.group({
          Id: new FormControl(0),
          Animal: new FormControl(null),
          Tagno: new FormControl(null),
        })
      );
    }
  }

  get formArray() {
    return this.reactForm.get('data') as FormArray;
  }

  onSubmit(): void {
    if (this.reactForm.invalid) {
      this.reactForm.markAllAsTouched();
    }
    alert(JSON.stringify(this.reactForm.value, null, 4));
    // ... validation code ...
    this.service.postOffspring(this.reactForm.value).subscribe({
      next: (response) => {
        console.log('Forms saved successfully', response);
      },
      error: (error) => {
        console.error('Error saving forms', error);
      },
    });
  }
}

bootstrapApplication(App, {
  providers: [provideHttpClient()],
});


HTML:

<div class="col-lg-12 control-section">
  <div class="content-wrapper" style="max-width: 600px; margin: 0 auto">
    <select [(ngModel)]="val" (change)="generateForms(val)">
      <option *ngFor="let item of unitData" [value]="item.Form">
        {{item.Form}}
      </option>
    </select>
    <form
      [formGroup]="reactForm"
      (ngSubmit)="onSubmit()"
      class="form-horizontal"
      novalidate
    >
      <div formArrayName="data">
        <div *ngFor="let form of formArray.controls; let i = index">
          <div [formGroupName]="i">
            <div class="form-title">
              <span>Customer {{ i + 1 }}</span>
            </div>

            <div class="form-group">
              <div class="e-float-input">
                <input [id]="'Id' + i" formControlName="Id" />
                <span class="e-float-line"></span>
                <label [for]="'Id' + i" class="e-float-text"></label>
              </div>
            </div>

            <div class="form-group">
              <div class="e-float-input">
                <input
                  [id]="'Animal' + i"
                  type="text"
                  formControlName="Animal"
                />
                <span class="e-float-line"></span>
                <label [for]="'Animal' + i" class="e-float-text">Animal</label>
              </div>
            </div>

            <div class="form-group">
              <div class="e-float-input">
                <input [id]="'Tagno' + i" type="text" formControlName="Tagno" />
                <span class="e-float-line"></span>
                <label [for]="'Tagno' + i" class="e-float-text">Tagno</label>
              </div>
            </div>
          </div>
        </div>
      </div>
      <button type="submit">submit</button>
    </form>
  </div>
</div>

Stackblitz Demo

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

3 Comments

@Samal you need to reapply what you fetch from the database, without working example details on what backend, it will be difficult to help you, for now, this is the working frontend based on the question details
The argument of the API should be the same that the "data" you send. e.g. If you send an array: public void Post([FromBody] Animaldb[] books) -see that you should loop over the array to add all the elements-. In the code futhermore, your should send reactForm.value.data -because the "data" is who have the array. BTW, it's NOT necessary create a FormGroup to mannage a FormArray, you can use directly a FormArray
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.