如何在 angular 13 中使用反应形式从动态输入中获取数据

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

我目前正在做一个 angular 项目,员工将从 angular 日期选择器中选择一个日期,然后 table 会弹出一个显示该月和每一天的日期他可以输入 1 或 0 的输入取决于他那天是否工作。 我的问题是我不知道如何获取用户输入的数据并在他单击保存按钮时控制台记录该数据

注意:StackBlitz 上的日期选择器有点奇怪(我没有导入一些包)我不知道如何修复它但在我的应用程序中它工作得很好我很抱歉,只是选择了一个日期和table会根据输入的日期变化, 我的 ts 文件:

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");
  }

}

我的 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;
}

我的 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>

这是我的 StackBlitz:stackBlitz

动态反应形式

HTML

在您的 html 中,您想确保表单知道哪些输入属于表单中的哪些值,您可以使用 formGroup、formArrayName、formGroupName、formControlName 指令...

例如:

<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>

打字稿

对于动态表单,您希望在表单中创建一个数组,您可以向其中添加表单组(最好通过调用函数)。这样您就可以遍历从 BE 返回的数据(天)并调用该函数。

例如:

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)
  }
}

请注意,您可以打印表单中包含的值,因为现在它们已正确绑定。

这应该让您大致了解了它的要点。如果您运行遇到更多问题或有任何疑问,请留言。

这里有一个Stackblitz给你玩。