像Excel一样基于鼠标和Shift选择集合Mat表行

问题描述 投票:0回答:1

我使用角度材料表,我的表包含一个选择模型来处理选定的全部和选定的一个,并包含一些列和一些行。 我现在所拥有的,我可以选择全部或选择一个元素,一切都很好, 我需要的是处理事件 Shift 单击作为 Excel 以选择多行复选框。 当我同时选择一个集合时,将选中复选框,并且我可以取消选中一个,然后单击它。(我在 ag 网格表中发现了此行为,但我需要使用 mat 表) 这个gif解释了行为,只是我需要选择start end之间的元素,我可以选择范围并将它们推入fianlArray..

行为

我的Html代码是这样的:

<ng-container matColumnDef="select">
  <th mat-header-cell *matHeaderCellDef>
    <mat-checkbox 
      id="allCheck" 
      (change)="setAllChekedOrUnchecked($event)"
      [checked]="isAllElementSelected" 
      [indeterminate]="iSsomeElementsSelected">
    </mat-checkbox>
  </th>
  <td mat-cell *matCellDef="let row">
    <mat-checkbox 
      [checked]="row.complete" 
      (click)="oneElementSelected($event, row)">
    </mat-checkbox>
  </td>
</ng-container>
<ng-container matColumnDef="entityCode">
  <th mat-header-cell *matHeaderCellDef>TOUTES</th>
  <td mat-cell *matCellDef="let element" style="cursor: pointer">
    <span (click)="openConfirmationDialog(element)">{{ element.entityCode }}</span>
  </td> 
</ng-container>`

我的 ts 文件:

dataSource!: MatTableDataSource<any>;
selection = new SelectionModel<Interclassement>(true, []);
selectedElements: Interclassement[] = [];

setAllChekedOrUnchecked($event: MatLegacyCheckboxChange) {
  this.dataSource.data.forEach(elt => {
    elt.complete = $event.checked;
  });
  this.isAllElementSelected = $event.checked;
  
  if ($event.checked) {
    this.selection.select(...this.dataSource.data);
  } else {
    this.selection.clear();
  }
  
  this.iSsomeElementsSelected = this.someElementsSelected();
}

iSsomeElementsSelected: boolean = false;

someElementsSelected() {
  return this.selection.selected.some(t => t.complete && !this.isAllElementSelected);
}

oneElementSelected($event: any, row: any) {
  if ($event.checked) {
    this.selection.select(row);
  } else {
    this.selection.deselect(row);
  }
  
  row.complete = $event.checked;
  this.isAllElementSelected = this.selection.selected.length === this.dataSource.data.length;
  this.iSsomeElementsSelected = this.someElementsSelected();
}

// Shift+Click logic : but it doesn't work.
lastSelectedId: number | null = null;

getRowRange(idA: number, idB: number): any[] {
  const rows = this.dataSource.data;
  const startIndex = rows.findIndex(row => row.id === idA);
  const endIndex = rows.findIndex(row => row.id === idB);

  if (startIndex === -1 || endIndex === -1) {
    throw new Error('Invalid row range');
  }

  const [from, to] = startIndex < endIndex ? [startIndex, endIndex] : [endIndex, startIndex];
  return rows.slice(from, to + 1);
}

toggleSelection(event: MouseEvent, row: any) {
  if (event.shiftKey && this.lastSelectedId !== null) {
    const rowsToToggle = this.getRowRange(this.lastSelectedId, row.id);
    const shouldSelect = !this.selection.isSelected(rowsToToggle[0]);

    rowsToToggle.forEach(row => {
      if (shouldSelect) {
        this.selection.select(row);
      } else {
        this.selection.deselect(row);
      }
    });
  } else {
    this.selection.toggle(row);
  }

  this.lastSelectedId = row.id;
}
javascript angular angular-material
1个回答
0
投票

是的,可以在 Angular Material 表(mat-table)中实现 Shift + 单击功能以进行多选行。尽管 Angular Material 的选择模型本身不支持基于移位的选择(如 Excel 或 ag-Grid),但您可以通过跟踪选定的行范围并相应地更新选择来手动处理此问题。

ts 文件

import { Component } from '@angular/core';
import { SelectionModel } from '@angular/cdk/collections';
import { MatTableDataSource } from '@angular/material/table';

@Component({
  selector: 'app-table-selection',
  templateUrl: './table-selection.component.html',
  styleUrls: ['./table-selection.component.css']
})
export class TableSelectionComponent {
  displayedColumns: string[] = ['select', 'name'];
  dataSource = new MatTableDataSource<Element>(ELEMENT_DATA);
  selection = new SelectionModel<Element>(true, []);
  
  lastSelectedIndex: number = -1;

  /** Whether the number of selected elements matches the total number of rows. */
  isAllSelected() {
    const numSelected = this.selection.selected.length;
    const numRows = this.dataSource.data.length;
    return numSelected === numRows;
  }

  /** Selects all rows if they are not all selected; otherwise clear selection. */
  masterToggle() {
    this.isAllSelected()
      ? this.selection.clear()
      : this.dataSource.data.forEach(row => this.selection.select(row));
  }

  /** The label for the checkbox on the passed row */
  checkboxLabel(row?: Element): string {
    if (!row) {
      return `${this.isAllSelected() ? 'deselect' : 'select'} all`;
    }
    return `${this.selection.isSelected(row) ? 'deselect' : 'select'} row ${row.name}`;
  }

  /** Handles selection based on shift-click functionality */
  handleRowSelection(event: MouseEvent, row: Element, index: number) {
    if (event.shiftKey && this.lastSelectedIndex !== -1) {
      const start = Math.min(this.lastSelectedIndex, index);
      const end = Math.max(this.lastSelectedIndex, index);
      for (let i = start; i <= end; i++) {
        this.selection.select(this.dataSource.data[i]);
      }
    } else {
      this.selection.toggle(row);
    }
    this.lastSelectedIndex = index;
  }
}

export interface Element {
  name: string;
}

const ELEMENT_DATA: Element[] = [
  { name: 'Hydrogen' },
  { name: 'Helium' },
  { name: 'Lithium' },
  { name: 'Beryllium' },
  { name: 'Boron' },
];

HTML 文件

<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
  <!-- Checkbox Column -->
  <ng-container matColumnDef="select">
    <th mat-header-cell *matHeaderCellDef>
      <mat-checkbox
        (change)="$event ? masterToggle() : null"
        [checked]="isAllSelected()"
        [indeterminate]="selection.hasValue() && !isAllSelected()">
      </mat-checkbox>
    </th>
    <td mat-cell *matCellDef="let row; let i = index">
      <mat-checkbox
        (click)="handleRowSelection($event, row, i)"
        [checked]="selection.isSelected(row)">
      </mat-checkbox>
    </td>
  </ng-container>

  <!-- Name Column -->
  <ng-container matColumnDef="name">
    <th mat-header-cell *matHeaderCellDef> Name </th>
    <td mat-cell *matCellDef="let element"> {{element.name}} </td>
  </ng-container>

  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
  <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
© www.soinside.com 2019 - 2024. All rights reserved.