Skip to content
Advertisement

how to implement angular table with reactive form

based on my html below , as you can see I have a table and then looped through mat cell using *matCellDef="let model" , inside the cell there are input fields which are reactive forms (it has mat error checking ,formcontrolname etc).

Each cell or row should have its own reactive form since each row has its own mat form fields input fields and validations.

How do we address this in angular?. I have provided some code snippets below that might help. highly appreciated. Thanks.

#html code

<mat-table [dataSource]="table.dataSource" [@animateStagger]="{value:'50'}" matMultiSort
    (matSortChange)="table.onSortEvent()">
    <ng-container matColumnDef="id">
        <mat-header-cell *matHeaderCellDef mat-multi-sort-header="id">
            <span class="text-left">COLUMN 1</span>
        </mat-header-cell>
        <mat-cell *matCellDef="let model" class="p-2">
            <form [formGroup]="modelForm">
                <div fxLayout="row" class="inspection-schedule-property-border-bottom" style="width: 99%;">
                    <div fxFlex="40" class="inspection-schedule-property-header-sub-label">
                        <mat-form-field appearance="fill" style="padding-right: 8px;" fxFlex="40">
                            <mat-label>
                                enter date
                            </mat-label>
                            <mat-date-range-input>
                                <input matStartDate placeholder="start date" required formControlName="start">
                                <input matEndDate placeholder="end date" required formControlName="end">
                            </mat-date-range-input>
                            <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
                            <mat-date-range-picker #picker></mat-date-range-picker>
                            <mat-error *ngIf="modelForm.controls.start.hasError('required')">Start date is
                                required</mat-error>
                            <mat-error *ngIf="modelForm.controls.end.hasError('required')">End date is
                                required</mat-error>
                        </mat-form-field>
            </form>

        </mat-cell>
    </ng-container>
</mat-table>

#ts code

modelForm: FormGroup;

 this.modelForm = this._createModelForm();

      private createModelForm(): FormGroup {
        return this.formBuilder.group({
           start: new FormControl(Validators.required),
           end: new FormControl(Validators.required),
        });
      }

#code that pulls data from api to be populated on table

 ngOnInit(): void {

    this.table.dataSource = new MatMultiSortTableDataSource(this.sort, this.CLIENT_SIDE);
    this.table.nextObservable.subscribe(() => { this.getDetails();});
    this.table.sortObservable.subscribe(() => { this.getDetails();});
    this.table.previousObservable.subscribe(() => {this.getDetails();});
    this.table.sizeObservable.subscribe(() => {this.getDetails();});
  }


  private getDetails() {    
    this.isLoading = true;
    this.service
    .getDetails(this.filterModel)
    .pipe(finalize(() => (this.isLoading = false)))
    .subscribe({
      next: (res) => { 
        this.table.data = res.items as Array<any>;
        this.table.totalElements = res.totalItemCount;   
      },
      error: (err) => {
        
      },
      complete: noop,
    });
  }

Advertisement

Answer

We have a ngx-multi-sort-table. The tecnica is similar

If we see in github the source

We see that we can override the function “_sortData” that belong to the class “MatMultiSortTableDataSource”, so we can do some like this in initData

  initData() {
    this.table.dataSource = new MatMultiSortTableDataSource(this.sort, this.CLIENT_SIDE);
    this.table.dataSource._sortData=(d1: AbstractControl, d2: AbstractControl, params: string[], dirs: string[]): number =>{
      if (d1.value[params[0]] > d2.value[params[0]]) {
          return dirs[0] === 'asc' ? 1 : -1;
      } else if (d1.value[params[0]] < d2.value[params[0]]) {
          return dirs[0] === 'asc' ? -1 : 1;
      } else {
          if (params.length > 1) {
              params = params.slice(1, params.length);
              dirs = dirs.slice(1, dirs.length);
              return this.table.dataSource._sortData(d1, d2, params, dirs);
          } else {
              return 0;
          }
      }
    }
    ....
  }

And when create the data we use some like

  this.table.data = res.users.map(x=>this.createGroup(x));

Another option is create a class that extends from “MatMultiSortTableDataSource”

export class MatMultiSortTableDataSourceControls<T> extends MatMultiSortTableDataSource<T> {
  constructor(sort: MatMultiSort, clientSideSorting = false) {
    super(sort,clientSideSorting);
  }
  _sortData=(dd1: T, dd2: T, params: string[], dirs: string[]): number =>{
    const d1=dd1 as any
    const d2=dd2 as any
    if (d1.value[params[0]] > d2.value[params[0]]) {
        return dirs[0] === 'asc' ? 1 : -1;
    } else if (d1.value[params[0]] < d2.value[params[0]]) {
        return dirs[0] === 'asc' ? -1 : 1;
    } else {
        if (params.length > 1) {
            params = params.slice(1, params.length);
            dirs = dirs.slice(1, dirs.length);
            return this._sortData(d1, d2, params, dirs);
        } else {
            return 0;
        }
    }
  }
}

And use

this.table.dataSource=new MatMultiSortTableDataSourceControls(this.sort,this.CLIENT_SIDE)

See a stackblitz

User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement