首页 > 解决方案 > 用于多行删除的 Angular 可重用材料表复选框

问题描述

我是 Angular 的初学者。在我的项目中,根据要求,我们为 Angular 创建了一个可重用的材料表组件。在此表中,我们在各个表行上具有诸如编辑和删除之类的选项。
现在,我们收到了在数据表中实现 Checkbox 的要求,以便可以选择和删除多行。
我们正在使用 NGRX 进行应用程序状态管理。

关于这一点,如果我能得到一些关于如何实现它的指导,那将非常有帮助。

我试图在表格中添加复选框。
但是,我无法弄清楚如何在组件级别实现删除操作。

//Data-Table
import { Component, OnInit, Input, Output, EventEmitter, SimpleChanges, OnChanges, ViewChild } from '@angular/core';
import { MatTableDataSource, MatPaginator } from '@angular/material';
import { FormGroup, FormControl, ValidatorFn } from '@angular/forms';
import * as moment from 'moment';
import {SelectionModel} from '@angular/cdk/collections';

export enum ColumnTypes {
  ColumnTypeReadonly = 0,
  ColumnTypeLink = 1,
  ColumnTypeEdit = 2,
  ColumnTypeProgress = 3,
  ColumnTypeDate = 4,
}

export enum ColumnEditTypes {
  EditTypeText = 0,
  EditTypeNumeric = 1,
  EditTypeBoolean = 3,
  EditTypeDate = 4,
}

export interface ColumnDef {
  id: string;
  text: string;
  columnType?: ColumnTypes;
  editType?: ColumnEditTypes; // If ColumnType is ColumnTypeEdit
  editValidations?: ValidatorFn[];
  width?: string;
  requiredColumn?: boolean;
  hideColumn?: boolean;
}
@Component({
  selector: 'app-data-table',
  templateUrl: './data-table.component.html',
  styleUrls: ['./data-table.component.scss']
})
export class DataTableComponent implements OnInit, OnChanges {
  showFiller = false;
  @Input() loading = false;
  @Input() data: any = null;
  @Input() displayColumns: ColumnDef[] = [];
  @Input() disableEdit: boolean;
  @Input() disableDelete: boolean;
  @Input() showEdit: boolean;
  @Input() showDelete: boolean;
  @Input() showPaginator: boolean;
  @Input() showHorizontalScroll: boolean;
  @Input() hasInlineEditor: boolean;
  @Output() dataAction = new EventEmitter();
  @Output() linkAction = new EventEmitter();
  @Output() highlightRow = new EventEmitter();

  //checkbox
  @Input() showCheckbox: boolean;
  @Output() removeSelected = new EventEmitter();

  @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;

  dataSource: MatTableDataSource<any> = null;
  columns: ColumnDef[] = [];
  columnsIds: string[] = [];
  isEditorOpen = false;
  inlineEditIndex: number;
  inlineEditObj: any;
  recordsFormGroup: FormGroup;
  highlightedRow: any;
  rowWidth = '100%';

  //checkbox
  selection = new SelectionModel<MatTableDataSource<any>>(true, []);


  constructor() { }

  ngOnInit() {
    this.columns = this.displayColumns.filter((col, idx) => {
      if (idx <= 0) {
        col.requiredColumn = true;
      } else {
        col.requiredColumn = false;
      }

      return !col.hideColumn;
    });

    this.columnsIds.push('colorCode');
    this.columns.map(col => {
      this.columnsIds.push(col.id);
    });

    if (this.showEdit || this.showDelete) {
      this.columnsIds.push('action');
    }

    //Checkbox
    if (this.showCheckbox) {
      this.columnsIds.push('select');
    }

    if (this.showHorizontalScroll === true) {
      this.displayColumns.forEach(col => {
        if (!col.hideColumn) {
          // tslint:disable-next-line:radix
          this.rowWidth = parseInt(this.rowWidth) + parseInt(col.width) + 'px';
        }
      });
      // tslint:disable-next-line:radix
      this.rowWidth = parseInt(this.rowWidth) + 100 + 'px'; // action column
    }
  }

  ngOnChanges(changes: SimpleChanges): void {
    this.dataSource = new MatTableDataSource(this.data);
    this.createInlineEditForm(this.data);

    if (this.showPaginator) {
      this.dataSource.paginator = this.paginator;
    }
  }

  createInlineEditForm(recordData: any) {
    this.recordsFormGroup = new FormGroup({});

    if (recordData) {
      recordData.map((record, idx) => {
        this.displayColumns.map(column => {
          if (column.columnType === ColumnTypes.ColumnTypeEdit) {
            const control: FormControl = new FormControl(record[column.id], column.editValidations);

            this.recordsFormGroup.addControl(column.id + '-' + idx, control);
          }
        });
      });
    }
  }

  onDataAction($event) {
    switch ($event.action) {
      case 'edit-action':
        this.isEditorOpen = false;
        this.inlineEditIndex = -1;
        this.dataAction.emit($event);
        break;
      case 'delete-action':
        this.isEditorOpen = false;
        this.inlineEditIndex = -1;
        this.dataAction.emit($event);
        break;
      case 'inline-edit-action':
        this.isEditorOpen = true;
        this.inlineEditIndex = $event.index;
        this.inlineEditObj = $event.obj;
        break;
      case 'inline-delete-action':
        this.isEditorOpen = false;
        this.inlineEditIndex = -1;
        this.dataAction.emit($event);
        break;
      case 'inline-edit-ok-action':
        if (!this.recordsFormGroup.valid) {
          return;
        }

        this.isEditorOpen = false;
        this.inlineEditIndex = -1;

        this.dataAction.emit({
          index: $event.index + (this.paginator.pageIndex * 10),
          action: $event.action,
          obj: {
            originalObj: $event.obj,
            formObj: this.createFormDataObj($event.obj, $event.index + (this.paginator.pageIndex * 10))
          }
        });
        break;
      case 'inline-edit-cancel-action':
        this.resetFormDataObj($event.obj, $event.index + (this.paginator.pageIndex * 10));

        this.isEditorOpen = false;
        this.inlineEditIndex = -1;
        break;
    }
  }

  createFormDataObj(recordData: any, rowIdx: number) {
    const formObj = { ...recordData };

    this.columns.map((column) => {
      if (column.columnType === ColumnTypes.ColumnTypeEdit) {
        formObj[column.id] = this.recordsFormGroup.controls[column.id + '-' + rowIdx].value;
      }
    });

    return formObj;
  }

  resetFormDataObj(recordData: any, rowIdx: number) {
    this.columns.map((column) => {
      if (column.columnType === ColumnTypes.ColumnTypeEdit) {
        this.recordsFormGroup.controls[column.id + '-' + rowIdx].setValue(recordData[column.id]);
      }
    });
  }

  onLinkAction(data: any) {
    this.linkAction.emit(data);
  }

  onHighlightRow(data) {
    this.highlightedRow = data;

    this.highlightRow.emit(data);
  }

  onPageChange($event) {
    if ($event.previousPageIndex !== $event.pageIndex) {
      if (this.isEditorOpen) {
        this.resetFormDataObj(this.inlineEditObj, this.inlineEditIndex + ($event.previousPageIndex * 10));

        this.isEditorOpen = false;
        this.inlineEditIndex = -1;
      }
    }
  }

  onColumnSelectMenuClick($event, selectedCol: ColumnDef) {
    this.displayColumns.map(displayColumn => {
      if (displayColumn === selectedCol) {
        displayColumn.hideColumn = !displayColumn.hideColumn;
      }

      return displayColumn;
    });

    $event.stopPropagation();
    return false;
  }

  displayColumnMenuClosed() {
    this.columns = this.displayColumns.filter(col => !col.hideColumn);

    this.columnsIds = ['colorCode'];
    this.columns.map(col => {
      this.columnsIds.push(col.id);
    });

    if (this.showEdit || this.showDelete) {
      this.columnsIds.push('action');
    }
  }

  formatDate(date) {
    const dt = moment(date, 'MM-DD-YYYY');

    return dt.isValid() ? dt.format('MM-DD-YYYY') : '';
  }


  //checkbox
  isAllSelected() {
    const numSelected = this.selection.selected.length;
    const numRows = this.dataSource.data.length;
    return numSelected === numRows;
  }

  masterToggle() {
    this.isAllSelected() ?
        this.selection.clear() :
        this.dataSource.data.forEach(row => this.selection.select(row));
  }

  /* removeSelectedRows() {

    this.selection.selected.forEach(item => {
      let index: number = this.data.findIndex(d => d === item);
      console.log(this.data.findIndex(d => d === item));
      this.data.splice(index,1)
      this.dataSource = new MatTableDataSource<any>(this.data);
    });
    this.selection = new SelectionModel<MatTableDataSource<any>>(true, []);
  } */

}


//HTML

<div class="mat-elevation-z2">
  <ng-container *ngIf="loading === true">
    <div class="loading">
      <mat-spinner></mat-spinner>
    </div>
  </ng-container>
  <form [formGroup]="recordsFormGroup">
    <ng-container *ngIf="loading === false">
      <mat-table
        #table
        [dataSource]="dataSource"
        (mouseout)="onHighlightRow(null)"
        [ngStyle]="
          showHorizontalScroll === true
            ? { 'overflow-x': 'scroll' }
            : { 'overflow-x': 'hide' }
        "
      >
      <ng-container *ngIf="showCheckbox" matColumnDef="select">
        <th mat-header-cell *matHeaderCellDef>
          <mat-checkbox (change)="$event ? masterToggle() : null"
                        [checked]="selection.hasValue() && isAllSelected()"
                        [indeterminate]="selection.hasValue() && !isAllSelected()"
                        >
          </mat-checkbox>
        </th>
        <td mat-cell *matCellDef="let row">
          <mat-checkbox (click)="$event.stopPropagation()"
                        (change)="$event ? selection.toggle(row) : null"
                        [checked]="selection.isSelected(row)"
                        >
          </mat-checkbox>
        </td>
      </ng-container>
        <ng-container matColumnDef="colorCode">
          <mat-header-cell
            *matHeaderCellDef
            style="max-width: 24px; padding-left: 0px;"
          >
          </mat-header-cell>
          <mat-cell *matCellDef="let element" class="colorCode">
            <div
              [ngStyle]="
                element.recordStatus && element.recordStatus === 'add'
                  ? { 'background-color': 'greenyellow', width: '2px' }
                  : element.recordStatus === 'edit'
                  ? { 'background-color': 'gold', width: '2px' }
                  : { 'background-color': 'transparent', width: '2px' }
              "
            ></div>
          </mat-cell>
        </ng-container>
        <ng-container
          [matColumnDef]="col.id"
          *ngFor="let col of columns; let colIdx = index"
        >
          <mat-header-cell
            class="mat-elevation-z2-table"
            *matHeaderCellDef
            [ngStyle]="
              showHorizontalScroll === true && { flex: '0 0 ' + col.width }
            "
          >
            {{ col.text }}
          </mat-header-cell>
          <ng-template
            [ngIf]="col.columnType === undefined || col.columnType === 0"
          >
            <!-- Readonly -->
            <mat-cell
              *matCellDef="let element"
              [ngStyle]="
                showHorizontalScroll === true && { flex: '0 0 ' + col.width }
              "
            >
              {{ element[col.id] }}
            </mat-cell>
          </ng-template>
          <ng-template [ngIf]="col.columnType === 1">
            <!-- Link -->
            <mat-cell
              *matCellDef="let element"
              [ngStyle]="
                showHorizontalScroll === true && { flex: '0 0 ' + col.width }
              "
            >
              <b (click)="onLinkAction(element)" style="cursor: pointer;">{{
                element[col.id]
              }}</b>
            </mat-cell>
          </ng-template>
          <ng-template [ngIf]="col.columnType === 2">
            <!-- Inline Edit -->
            <mat-cell
              *matCellDef="let recordData; let idx = index"
              [ngStyle]="
                showHorizontalScroll === true && { flex: '0 0 ' + col.width }
              "
            >
              <app-edit-input-wrapper
                [isEditorOpen]="isEditorOpen && idx === inlineEditIndex"
                [readValue]="recordData[col.id]"
                [isValid]="
                  !recordsFormGroup.controls[
                    [col.id] + '-' + (idx + paginator.pageIndex * 10)
                  ].invalid
                "
              >
                <ng-template [ngIf]="col.editType === 0">
                  <input
                    [formControlName]="
                      [col.id] + '-' + (idx + paginator.pageIndex * 10)
                    "
                    appInputSelectAll
                  />
                </ng-template>
                <ng-template [ngIf]="col.editType === 1">
                  <input
                    [formControlName]="
                      [col.id] + '-' + (idx + paginator.pageIndex * 10)
                    "
                    type="number"
                    appInputSelectAll
                    appOnlyNumber
                  />
                </ng-template>
              </app-edit-input-wrapper>
            </mat-cell>
          </ng-template>
          <ng-template [ngIf]="col.columnType === 4">
            <!-- Date -->
            <mat-cell
              *matCellDef="let element"
              [ngStyle]="
                showHorizontalScroll === true && { flex: '0 0 ' + col.width }
              "
            >
              {{ formatDate(element[col.id]) }}
            </mat-cell>
          </ng-template>
        </ng-container>
        <ng-container *ngIf="showEdit || showDelete" matColumnDef="action">
          <mat-header-cell
            *matHeaderCellDef
            class="mat-elevation-z2-table"
            style="max-width: 100px; display: flex; align-items: center; justify-content: flex-end; padding-right: 0px;"
          >
            <mat-icon
              style="font-size: large; color: red; display: flex; align-items: center; justify-content: flex-end;"
              *ngIf="!recordsFormGroup.valid"
              >warning</mat-icon
            >
            <button
              mat-button
              [matMenuTriggerFor]="menu"
              (menuClosed)="displayColumnMenuClosed()"
            >
              <mat-icon>list</mat-icon>
            </button>
            <mat-menu #menu="matMenu" yPosition="above">
              <div
                #menu
                mat-menu-item
                disableRipple="true"
                [disabled]="displayColumn.requiredColumn"
                *ngFor="let displayColumn of displayColumns"
                (click)="onColumnSelectMenuClick($event, displayColumn)"
              >
                <mat-checkbox
                  type="checkbox"
                  [checked]="!displayColumn.hideColumn"
                  [disabled]="displayColumn.requiredColumn"
                ></mat-checkbox>
                {{ displayColumn.text }}
              </div>
            </mat-menu>
          </mat-header-cell>
          <ng-container>
            <mat-cell
              *matCellDef="let recordData; let idx = index"
              style="max-width: 100px; max-width: 100px; display: flex; align-items: center; justify-content: flex-end; margin-right: -12px;"
            >
              <app-action-buttons
                [disableEdit]="disableEdit"
                [disableDelete]="disableDelete"
                [showEdit]="showEdit"
                [showDelete]="showDelete"
                [hasInlineEditor]="hasInlineEditor"
                [recordData]="recordData"
                [recordIndex]="idx"
                [isEditorOpen]="isEditorOpen"
                [inlineEditIndex]="inlineEditIndex"
                (dataAction)="onDataAction($event)"
              >
              </app-action-buttons>
            </mat-cell>
          </ng-container>
        </ng-container>
        <mat-header-row
          *matHeaderRowDef="columnsIds"
          [ngStyle]="{ width: rowWidth }"
        ></mat-header-row>
        <mat-row
          *matRowDef="let row; columns: columnsIds; let idx = index"
          (mouseover)="onHighlightRow(row)"
          [ngClass]="
            idx == inlineEditIndex
              ? 'editor'
              : row == highlightedRow && 'highlight'
          "
          [ngStyle]="{ width: rowWidth }"
        >
        </mat-row>
      </mat-table>
    </ng-container>
    <div [hidden]="!showPaginator || dataSource.data.length <= 10">
      <mat-paginator
        #paginator
        [pageSizeOptions]="[10]"
        [disabled]="loading"
        showFirstLastButtons
        (page)="onPageChange($event)"
      ></mat-paginator>
    </div>
  </form>
</div>

标签: angularangular-material

解决方案


在您的组件中,您触发一个删除事件,例如

      @Output() delete= new EventEmitter<any>();
      ...
      onDelete() {
         this.delete.emit(data);
      }

然后你在超级组件中捕捉到它

              <app-action-buttons
                [disableEdit]="disableEdit"
                [disableDelete]="disableDelete"
                [showEdit]="showEdit"
                [showDelete]="showDelete"
                [hasInlineEditor]="hasInlineEditor"
                [recordData]="recordData"
                [recordIndex]="idx"
                [isEditorOpen]="isEditorOpen"
                [inlineEditIndex]="inlineEditIndex"
                (dataAction)="onDataAction($event)"
                (delete)="onDelete($event)"
              >
              </app-action-buttons>

推荐阅读