angularangular-reactive-formsformarray

Handling tabs data in angular reactive forms


I am developing a calendar app and want to handle three tabs such as hours timing / out of office / holiday tabs in a form.
what is the efficient appraoch to group the form as there is one form and three tabs and only one button to create an event.

I am doing it in this way and initializing from in ngOnInit lifecycle hook and I have done it in this way.

this.eventForm = this.fb.group({
      businessHours: this.fb.group({
        title: ["Title", [Validators.required]],
        datePicker: [""],
        shifts: this.fb.array([]),
        calendars: ["", [Validators.required]],
        color: [""],
        endDateCriteria: ["never"],
        recurrenceCriteria: ["does not repeat"],
        dateRangeStart: [""],
        dateRangeEnd: [""],
        recurrenceRule: [""]
      }),
      outOfOffice: this.fb.group({
        // title: ['', Validators.required],
        // Add other controls as needed
      }),
      holiday: this.fb.group({
        // title: ['', Validators.required],
        // Add other controls as needed
      })
    });

This is where I am grouping a form with three different tabs

and in the below snippet I have the .html file code where I want to show the formcontrol. But now the issue here is that I got the error of all the formControls that `core.js:6210 ERROR Error: Cannot find control with name: 'title'`
Similar errors related to other controls.

<ng-template #eventFormVariable>
  <div class="dialog-container event-header">
    <div class="mat-card-header">
      <mat-icon (click)="onClose()">close</mat-icon>
      <h4 class="content-span full-width">{{formHeading}}</h4>
    </div>
    <div class="dialog-content event-dialog">
      <form class="form-horizontal form-bordered" [formGroup]="eventForm">


        <mat-tab-group animationDuration="0ms" (selectedTabChange)="onTabChanged($event);" [(selectedIndex)]="activeTabIndex">

          <mat-tab label="Business Hours">

            <div class="col-md-12 p-0">

              <div class="title-section event-fields">
                <mat-form-field [color]="underLineColor">
                  <input matInput formControlName="title">
                </mat-form-field>
              </div>


              <div class="shift-section event-fields">
                <mat-icon class="time-icon">access_time</mat-icon>

                <div class="datepicker-section">
                  <mat-label>Start Date</mat-label>
                  <mat-form-field [color]="underLineColor">
                    <input formControlName="datePicker" matInput [matDatepicker]="picker" [min]="yesterday" (click)="picker.open()">
                    <mat-datepicker #picker></mat-datepicker>
                  </mat-form-field>
                </div>

                <div formArrayName="shifts">
                  <div>
                   <div *ngFor="let shift of shifts.controls; let i = index" [formGroupName]="i" class="shift-main">
                      <mat-label>Shifts</mat-label>
                      <mat-form-field class="shift-title" [color]="underLineColor">
                        <input matInput type="text" class="shift-value" formControlName="shifts">
                      </mat-form-field>

                      <mat-form-field [color]="underLineColor">
                        <span class="time-select-value time-from">
                          <mat-select formControlName="selectedTimeFrom">
                            <mat-option *ngFor="let timeFrom of selectTime" [value]="timeFrom.value">
                              {{timeFrom.viewValue}}
                            </mat-option>
                          </mat-select>
                        </span>
                      </mat-form-field>

                      <mat-form-field [color]="underLineColor">
                        <span class="time-select-value">
                          <mat-select formControlName="selectedTimeTo">
                            <mat-option *ngFor="let timeTo of selectTime" [value]="timeTo.value">
                              {{timeTo.viewValue}}
                            </mat-option>
                          </mat-select>
                        </span>
                      </mat-form-field>

                      <button class="remove-btn" mat-icon-button (click)="removeShift(i)">
                        <mat-icon>close</mat-icon>
                      </button>
                    </div>
                  </div>
                </div>
                <button mat-raised-button class="shift-btn" (click)="addShift()">Add Shift</button>
              </div>


              <div class="recurrence-section event-fields" [ngClass]="{ 'repeat-custom-option': repeatOption === 'custom' }">
                <mat-icon>replay</mat-icon>
                <mat-label>Repeat</mat-label>
                <mat-form-field appearance="fill">
                  <mat-select #selectRepeat (selectionChange)="recurrence($event, customRecurrence)"
                               [(ngModel)]="repeatOption" [ngModelOptions]="{standalone: true}">
                    <mat-option *ngFor="let option of recurrenceOptionsForBusiness" [value]="option">
                      {{ option | titlecase }}
                    </mat-option>
                  </mat-select>
                </mat-form-field>
            
                <span class="repeat-custom-value" *ngIf="repeatOption === 'custom'" (click)="selectRepeat.open()">
                  {{ repeatType === 'week' ? 'Weekly on' : repeatType }}
                  <span class="week-values" *ngIf="repeatType === 'week'">
                    <span *ngFor="let day of weekDays"> {{ day }} </span>
                  </span>
                </span>
              </div>


              <div class="end-date-section event-fields {{eventForm.controls['endDateCriteria'].value === 'custom' ? 'repeat-custom-option':''}}" *ngIf="repeatOption !== 'does not repeat'">
                <mat-icon>check_circle_outline</mat-icon>
                <mat-label>Expiry Date</mat-label>
                <mat-form-field appearance="fill">
                  <mat-select #selectEndDate  formControlName="endDateCriteria" [(value)]="selected">
                    <mat-option value="never">Never</mat-option>
                    <mat-option value="custom" (click)="openEndDateModal(endDateReference)">Custom</mat-option>
                  </mat-select>
                </mat-form-field>

                <span class="repeat-custom-value" *ngIf="eventForm.controls['endDateCriteria'].value === 'custom'" (click)=selectEndDate.open()> {{endDate | date}}
                  </span>
              </div>
            </div>

          </mat-tab>
</mat-tab-group>

There are other tabs code as well I just have shortened it with respect to the single tab. I just want to know the effiecient appraoch with respect to handling the three tabs in a single form group . what that can be in angluar.

I am finding it difficult to handle three tabs in a single form as getting error related to formControls that these formcontrols are not found but i have used the same formControl and initiliazed it in ngOnInit.

How to efficiently handle the forms with multiple tabs so I can pick the values of the respective tab and process it further as I have a single submit button.


Solution

  • We can use the property preserveContent to true in mat-tab-group, which will prevent destroy of the elements when not visible, forms do not work well, when the elements are destroyed, also the elements are grouped under a formGroup (businessHours) so we need to specify a formGroupName="businessHours" for each mat-tab, so that forms will understand to which group the elements belong to!

    <div class="dialog-container event-header">
      <div class="mat-card-header">
        <mat-icon>close</mat-icon>
        <h4 class="content-span full-width">{{'formHeading'}}</h4>
      </div>
      <div class="dialog-content event-dialog">
        <form class="form-horizontal form-bordered" [formGroup]="eventForm">
          <mat-tab-group
            [preserveContent]="true" 
            animationDuration="0ms"
            [(selectedIndex)]="activeTabIndex"
          >
            <mat-tab label="Business Hours" formGroupName="businessHours">
              <div class="col-md-12 p-0">
                <div class="title-section event-fields">
                  <mat-form-field>
                    <input matInput formControlName="title" />
                  </mat-form-field>
                </div>
              </div>
            </mat-tab>
          </mat-tab-group>
        </form>
      </div>
    </div>
    
    {{eventForm.value | json}}
    

    Stackblitz Demo