Fouad_Aurag
Fouad_Aurag

Reputation: 35

how to get data from dynamic inputs using reactive forms in angular 13

im curently working on an angular project where an employee will chose a date from an angular datePicker and a table will popup showing the days of that month and under each day an input where he can enter 1 or 0 depends if he worked that day or not . my probleme is i dont know how to get the data that the user entered and console log that data wheneever he clicks on the save button

note : the date picker is a bit weird (im not importing some packages) on the StackBlitz i dont know how to fix it but in my app it workes just fine im sorry about that, just chose a date and the table will change according to the date entered, my ts file :

import { Component, OnInit } from '@angular/core';
import {FormControl, FormArray, FormGroup, FormBuilder} from '@angular/forms';
import {MomentDateAdapter, MAT_MOMENT_DATE_ADAPTER_OPTIONS} from '@angular/material-moment-adapter';
import {DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE} from '@angular/material/core';
import {MatDatepicker} from '@angular/material/datepicker';
import { MatDatepickerInputEvent } from '@angular/material/datepicker';
import { DatePipe } from '@angular/common'

import * as moment from 'moment';
import { Moment } from 'moment';

export const MY_FORMATS = {
  parse: {
    dateInput: 'MM/YYYY',
  },
  display: {
    dateInput: 'MM/YYYY',
    monthYearLabel: 'MMM YYYY',
    dateA11yLabel: 'LL',
    monthYearA11yLabel: 'MMMM YYYY',
  },
};


@Component({
  selector: 'app-project',
  templateUrl: './project.component.html',
  styleUrls: ['./project.component.css'],
  providers: [
    {
      provide: DateAdapter,
      useClass: MomentDateAdapter,
      deps: [MAT_DATE_LOCALE, MAT_MOMENT_DATE_ADAPTER_OPTIONS],
    },

    {provide: MAT_DATE_FORMATS, useValue: MY_FORMATS},
  ],
})
export class ProjectComponent implements OnInit {

  monthDates: String[] = [];
  daysOfMonth !: number ;
  daysArray !: FormGroup;

  constructor(public datePipe : DatePipe, private fb:FormBuilder) { }

  ngOnInit(): void {
    this.daysArray = this.fb.group({
      daysWorked : this.fb.array([
        new FormControl(''),
      ])
    })
    let today = new Date();
    let todayMonth = today.getMonth() +1;
    let todayYear = today.getFullYear() ;
    const month = Number(todayMonth);
    const year = Number(todayYear);
    console.log(month);
    console.log(year);
    this.monthDates = this.getDaysArray(year,month);
    this.daysOfMonth = this.monthDates.length; 
  }

  getDaysArray = (year: number, month: number) => {
    const names = Object.freeze(
       [ 'Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam' ]);
    const date = new Date(year, month - 1, 1);
    const result = [];
    while (date.getMonth() == month - 1) {
      result.push(`${date.getDate()}
      ${names[date.getDay()]}`);
      date.setDate(date.getDate() + 1);
    }
    return result;
  }

  date = new FormControl(moment());

  setMonthAndYear(normalizedMonthAndYear: Moment, datepicker: MatDatepicker<Moment>) {
    const ctrlValue = this.date.value;
    ctrlValue.month(normalizedMonthAndYear.month());
    ctrlValue.year(normalizedMonthAndYear.year());
    this.date.setValue(ctrlValue);
    datepicker.close();
  }

  dateChanged($event: MatDatepickerInputEvent<Date>) {
    let monthChosen = moment($event.target.value).format("MM");
    let yearChosen =moment($event.target.value).format("yyyy");
    let month = Number(monthChosen);
    let year = Number(yearChosen);
    this.monthDates = this.getDaysArray(year,month);
    this.daysOfMonth = this.monthDates.length;
  }
  
  addDaysWorked() {
    console.log("add Days Worked !");
  }

  editDaysWorked() {
    console.log("Edit Days Worked");
  }

}

my CSS :

.fixTop {
    margin-top: 100px;
    margin-bottom: 100px;
    margin-right: 100px;
    margin-left: 100px;
}
.form-control {
    /* padding: 30%; */
    padding-left: 10px;
    padding-right: 2px;
    size: 1;
}

.fixTable {
    margin-top: 50px;
    margin-right: 300px;
}
.top {
    margin-top: 100px;
}
.aaa{
    margin-inline: 15%;
}

.example-month-picker .mat-calendar-period-button {
    pointer-events: none;
}
  
  .example-month-picker .mat-calendar-arrow {
    display: none;
}
.right {
    margin-left: 200px;
}

.c5 {
    background-color: blue;
}

my HTML :

<div class="d-flex">
    <div class="ms-5 mx-5 col-1">
        <mat-form-field appearance="outline">
            <mat-label>Month and Year</mat-label>
            <input matInput [matDatepicker]="dp" [formControl]="date" (dateChange)="dateChanged($event)">
            <mat-datepicker-toggle matSuffix [for]="dp"></mat-datepicker-toggle>
            <mat-datepicker #dp
                            startView="year"
                            panelClass="example-month-picker">
            </mat-datepicker>
          </mat-form-field>
          
    </div>
    <div class="card shadow-sm p-3 right bg-body rounded aaa">
        <span>
            <span class="border pt-1 ps-4 bg-info me-1"></span> <span>Jours férié</span>
            <span class="border pt-1 ps-4 bg-success ms-5 me-1"></span> <span>RTT Employeur</span>
            <span class="border pt-1 ps-4 bg-danger ms-5 me-1"></span> <span>Absence ou congés</span>
            <span class="border pt-1 ps-4 bg-primary ms-5 me-1"></span> <span>RTT Salaré(e)</span>
        </span>
    </div>
</div>  

<div>
    <form>  
        <div class="mt-5">
            <div class="d-flex mx-5 me-5 mb-5">
                    <table class="table">
                        <thead>
                            <tr>
                            <th scope="col" *ngFor="let date of monthDates">{{ date }}</th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr>
                                <td *ngFor="let i of [].constructor(this.daysOfMonth)">
                                    <input class="form-control" type="text"  value="0" size="1" maxlength="1">
                                </td>
                            </tr>
                            <tr>
                                <td *ngFor="let i of [].constructor(this.daysOfMonth)">
                                    <input disabled class="form-control" type="text"  value="" size="1" maxlength="1">
                                </td>
                            </tr>
                        </tbody>
                    </table>
                <br>
            </div>
            <div [align]="'center'">
                <button mat-raised-button (click)="addDaysWorked()" color="primary" > save </button>
                <button class="ms-3" mat-raised-button (click)="editDaysWorked()" color="warn" > edit </button>
            </div>
        </div>
    </form>
</div>

this is my StackBlitz : stackBlitz

Upvotes: 2

Views: 3209

Answers (1)

H3AR7B3A7
H3AR7B3A7

Reputation: 5261

Dynamic Reactive Forms

HTML

In your html you want to make sure the form knows what inputs belong to what values in the form, you do this with the formGroup, formArrayName, formGroupName, formControlName directives...

For example:

<form novalidate (ngSubmit)="save()" [formGroup]="form">
  <div formArrayName="days" *ngFor="let day of days.controls; let i = index">
    <div [formGroupName]="i">
      <label [for]="i">
        <input
          class="form-check-input"
          [id]="i"
          type="text"
          [value]="form.value.days[i].day"
          formControlName="day"
          name="day"
        />
        Day {{ i }}
      </label>
    </div>
  </div>
  <button type="submit">Save</button>
</form>

Typescript

For dynamic forms you want to create an array in the form, to which you can add formgroups (preferably by calling a function). This way you can loop over the data (days) you get back from the BE and call the function.

For example:

export class AppComponent implements OnInit {
  
  form: FormGroup;

  get days(): FormArray {
    return this.form.get('days') as FormArray;
  }

  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    this.form = this.fb.group({
      days: this.fb.array([])
    });
    this.addDay() // when you get the days in a month you loop over them and call addDay()
    this.addDay() // I am just calling it twice here for the sake of brevity
  }

  addDay(): void {
    this.days.push(this.buildDay());
  }

  buildDay(): FormGroup {
    return this.fb.group({
      day: '0' // Default can also be ''
    });
  }

  save() {
    console.log(this.form.value)
  }
}

Notice that you can print the values contained in the form because now they are properly bound.

This should give you the gist of it. If you run into more problems or have any questions, just give a shout.

Here is a Stackblitz for you to play with.

Upvotes: 1

Related Questions