FormArray inside a formArray in a table - Cannot find control with name :'0'

23 views Asked by At

Is what im tring to do

I have the following code but i don´t know what im doing wrong in the activities part , i spect to add n activities per employee having the projectActivity and percentage per activity, and im having lot of errors that i don´t know how to solve

<mat-card class="custom-card w-100">
  <mat-card-header>
    <mat-form-field class="form-field fb-100" appearance="outline">
      <mat-label>Proyecto</mat-label>
      <mat-select [formControl]="projectControl" required (selectionChange)="assignProject($event)">
        <mat-option *ngFor="let projectOption of currentProjects" [value]="projectOption.id">
          {{ projectOption.id }} - {{projectOption.projectName}}
        </mat-option>
      </mat-select>
    </mat-form-field>
  </mat-card-header>


</mat-card>
<form [formGroup]="assistanceForm">
  <table mat-table [dataSource]="employees" class="mat-elevation-z8">
    <ng-container matColumnDef="employee">
     ...
    </ng-container>

    <ng-container matColumnDef="date">
    ...
    </ng-container>

    <ng-container matColumnDef="startTime">
      <th mat-header-cell *matHeaderCellDef> Hora de inicio</th>
      <td mat-cell *matCellDef="let element">
        <mat-form-field class="form-field" appearance="outline">
          <input matInput [formControl]="element.get('startTime')" required type="time">
        </mat-form-field>
      </td>
    </ng-container>

    <ng-container matColumnDef="endTime">
      <th mat-header-cell *matHeaderCellDef> Hora de finalización</th>
      <td mat-cell *matCellDef="let element">
        <mat-form-field class="form-field" appearance="outline">
          <input matInput [formControl]="element.get('endTime')" required type="time">
        </mat-form-field>
      </td>
    </ng-container>

    <ng-container matColumnDef="activities" >
      <th mat-header-cell *matHeaderCellDef>Activities - %</th>
      <td mat-cell *matCellDef="let element; let i = index">
        <div class="f-center wrap" [formArrayName]="i">
          <div *ngFor="let activity of getEmployeeActivities(i)?.controls; let j = index; last as isLast;" [formGroupName]="j"
               class="f-center">
            <mat-form-field class="form-field activity-field" appearance="fill">
              <mat-select formControlName="projectActivity" required>
                <mat-option *ngFor="let activityOption of project?.activities"
                            [value]="activityOption.id">
                  {{ activityOption.id }} - {{activityOption.activityName}}
                </mat-option>
              </mat-select>
            </mat-form-field>
            -
            <mat-form-field class="form-field" appearance="fill">
              <input matInput formControlName="percentage" required min="0" max="100">
              <span matTextSuffix>%</span>
            </mat-form-field>

            <button mat-icon-button (click)="removeActivity(i, j)" *ngIf="getEmployeeActivities(i).length > 1">
              <mat-icon>delete</mat-icon>
            </button>
            <button mat-icon-button (click)="addActivity(i)" *ngIf="isLast">
              <mat-icon>add</mat-icon>
            </button>
          </div>

        </div>
      </td>
    </ng-container>

    <ng-container matColumnDef="permission">
      ..
    </ng-container>

    <ng-container matColumnDef="permissionHours">
     ...
    </ng-container>

    <ng-container matColumnDef="file">
  ...
    </ng-container>

    <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
    <tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
  </table>
</form>

i

mport {Component, OnInit, ViewEncapsulation} from '@angular/core';
import {AbstractControl, FormArray, FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms";
import {MatCheckboxChange} from "@angular/material/checkbox";
import {MatSlideToggleChange} from "@angular/material/slide-toggle";
import {Project} from "../../../../models/Project/project.interface";
import {ProjectsService} from "../../../../services/projects.service";
import {MatSelectChange} from "@angular/material/select";

@Component({
  selector: 'app-timesheet-project-form',
  templateUrl: './timesheet-project-form.component.html',
  styleUrls: ['./timesheet-project-form.component.scss'],
})
export class TimesheetProjectFormComponent implements OnInit {
  displayedColumns: string[] = ['employee', 'date', 'startTime', 'endTime', 'activities', 'permission',
    'permissionHours', 'file'
  ];

  predefinedEmployees = [
    {employeeID: '001', name: 'John Doe'},
    {employeeID: '002', name: 'Jane Smith'},
    // Add more employees as needed
  ];
  assistanceForm: FormGroup;
  projectControl = new FormControl('', Validators.required);
  currentProjects!: Project[];
  project: Project | undefined;

  constructor(private fb: FormBuilder,
              private projectsService: ProjectsService) {
    this.assistanceForm = this.fb.group({
      employees: this.fb.array([]),
    });

    this.setPredefinedEmployees();
  }

  async ngOnInit() {
    await this.getData();
  }

  async getData() {
    try {
      this.currentProjects = await this.projectsService.getProjects();
    } catch (e) {
      console.error(e)
    }
  }

  get employees() {
    return (this.assistanceForm.get('employees') as FormArray).controls;
  }

  setPredefinedEmployees() {
    const employeeAssistanceControls = this.assistanceForm.get('employees') as FormArray;

    this.predefinedEmployees.forEach((employee) => {
      const employeeGroup = this.fb.group({
        employee: [employee.employeeID],
        name: [employee.name],
        date: [new Date(), Validators.required],
        startTime: ['07:00', Validators.required],
        endTime: ['16:30', Validators.required],
        activities: this.fb.array([
          this.fb.group({
            projectActivity: '',
            percentage: '',
          })
        ]),
        permission: this.fb.control(false),
        absence: this.fb.control(false),
        permissionHours: [''],
        justification: this.fb.control(false),
        file: this.fb.control('')
      });

      employeeAssistanceControls.push(employeeGroup);
    });
    console.log(this.assistanceForm)
    console.log(this.employees.at(0))
  }

  addActivity(index: number) {
    const employee = this.employees.at(index);
    const newActivity = this.fb.group({
      projectActivity: '',
      percentage: '',
    });
    const activities = employee?.get('activities') as FormArray;
    activities.push(newActivity);
  }

  removeActivity(index: number, activityIndex: number) {
    const employee = this.employees.at(index);
    const activities = employee?.get('activities') as FormArray;
    if (activityIndex >= 0 && activityIndex < activities.length) {
      activities.removeAt(activityIndex);
    }
  }

  toggleAssistanceMod(key: string, index: number, event: MatCheckboxChange) {
    const employee = this.employees.at(index);
    const fieldToDisable = ['absence', 'permission'].find(field => field !== key);
    if (event.checked) {
      employee?.get(fieldToDisable!)?.disable()
    } else {
      employee?.get(fieldToDisable!)?.enable()
    }
    employee?.get(key)?.setValue(event.checked);

  }

  hasPermission(index: number): boolean {
    const employee = this.employees.at(index);
    return employee?.get('permission')?.value;
  }

  hasIncapacity(index: number): boolean {
    const employee = this.employees.at(index);
    return employee?.get('absence')?.value;
  }

  toggleJustification(event: MatCheckboxChange, index: number) {
    const employee = this.employees.at(index);
    employee?.get('justification')?.setValue(event.checked);
  }

  justified(index: number): boolean {
    const employee = this.employees.at(index);
    return employee?.get('justification')?.value;
  }


  onFileSelected(index: number, event: Event) {
    const files = (event.target as HTMLInputElement).files;
    if (files && files.length > 0) {
      const selectedFile = files[0];
      const employee = this.employees.at(index);
      employee?.get('file')?.setValue(selectedFile);
    }
  }

  deleteFile(index: number) {
    const employee = this.employees.at(index);
    const fileControl = employee?.get('file');

    if (fileControl?.value) {
      // Delete the attached file
      // You may want to add confirmation before deleting
      fileControl?.setValue(null);
    }
  }

  openFile(index: number) {
    const employee = this.employees.at(index);
    const attachedFile = employee?.get('file')?.value;

    if (attachedFile) {
      const fileReader = new FileReader();
      fileReader.onload = () => {
        const fileContent = fileReader.result;
        // Create an anchor element to trigger the file download
        const a = document.createElement('a');
        a.href = fileContent as string;
        a.download = attachedFile.name;
        a.style.display = 'none';
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
      };
      fileReader.readAsDataURL(attachedFile);
    }
  }

  getEmployeeActivities(index: number) {
    return this.employees.at(index)?.get('activities') as FormArray;
  }

  assignProject(event:MatSelectChange) {
    this.project = this.currentProjects.find(project => project.id === +this.projectControl!.value!);
  }
}


I have the following code but i don´t know what im doing wrong in the activities part , i spect to add n activities per employee having the projectActivity and percentage per activity, and im having lot of errors that i don´t know how to solve

I tried [formArray]="element.get('activities') and [formArrayName]="i"

0

There are 0 answers