Ian*_*ung 3 typescript angular
我正在尝试MatDataTable使用 angular 和 typescript在排序上实现 shift 单击功能。
基本原理是,只要在表上注册了单击事件,就会存储选定的行。
如果检测到 shift 单击,该组件将尝试在最后选定的行和当前选定的行之间进行选择(就像在 Windows 中使用 shift 单击选择一样)。
我的事件处理代码如下:
clickHandler(event, row, index) {
console.log('index clicked: ' + index);
if (event.ctrlKey) {
this.selectRows(row, index); // This records this.lastSelected
} else if (event.shiftKey) {
this.selectRowsFill(row, index);
} else {
this.selectElement(row, index); // As does this call.
}
}
// This function computes a start and end point for autoselection of
// rows between this.lastSelected, and the currently clicked row
selectRowsFill(row, index) {
const indexA = this.lastSelected;
const indexB = index;
if (indexA > indexB) {
// Descending order
this.selectRowsBetween(indexB, indexA);
} else {
// Ascending order
this.selectRowsBetween(indexA, indexB);
}
}
// And this performs the actual selection.
private selectRowsBetween(start, end) {
let currentIndex = 0;
this.dataSource.data.forEach(row => {
if (currentIndex >= start && currentIndex <= end) {
this.selection.select(row);
}
currentIndex++;
});
}
Run Code Online (Sandbox Code Playgroud)
和 HTML:
<mat-row *matRowDef="let row; let i = index; columns: cols;" (click)="clickHandler($event, row, i)" [ngClass]="{'inDatabase' : isAdded(row), 'highlight': isSelectedAndAdded(row) || isSelected(row) }">
Run Code Online (Sandbox Code Playgroud)
只要表格未排序,此代码就可以正常工作。一旦我将排序算法应用于MatTableDataSource,它就会更改数据的顺序,从而导致选择出现故障。看起来选择是基于 中数据的原始(未排序)顺序MatTableDataSource,这是有道理的。
那么如何让 shift 单击选择对排序的数据起作用,而不是对未排序的数据起作用?
这是我的解决方案:>STACKBLITZ<
<table [shiftClickSource]="dataSource"
[shiftClickSelectModel]="selection"
[shiftClickSourceId]="['position']"
mat-table [dataSource]="dataSource" class="mat-elevation-z8" matSort>
....
Run Code Online (Sandbox Code Playgroud)
import { Directive, Input, HostListener, OnInit, OnDestroy, ElementRef } from '@angular/core';
import {SelectionModel, SelectionChange} from '@angular/cdk/collections';
import { Subject, BehaviorSubject, Observable, merge, pipe, } from 'rxjs';
import { shareReplay, takeUntil, withLatestFrom, tap } from 'rxjs/operators';
import {MatTable, MatTableDataSource} from '@angular/material/table';
/**
* Directive that adds shift-click selection to your mat-table.
* It needs the datasource from the host, the selectionModel of the checkboxes
* and optionally an array of [ids] to find the correct rows in the
* (possibly sorted/ filtered) source array.
*/
@Directive({
selector: '[shiftClickSource]'
})
export class ShiftClickDirective implements OnInit, OnDestroy {
finishHim = new Subject<void>();
lastItem: any;
lastShiftSelection: any[];
alwaysTheRightSourceArray$: Observable<any>;
// the always right source array :-)
source: any[];
shiftHolding$ = new BehaviorSubject<boolean>(false);
//click that happens on tbody of mat-table
click$ = new Subject<void>();
// observable to record the last change of the checkbox selectionModel
recordSelectionChange$: Observable<SelectionChange<any>>;
@HostListener('document:keydown.shift', ['$event'])
shiftDown(_) {
this.shiftHolding$.next(true);
}
@HostListener('document:keyup.shift', ['$event'])
shiftUp(event: KeyboardEvent) {
this.shiftHolding$.next(false);
}
// datasource that is used on the Checkbox-Table
@Input('shiftClickSource') dataSource: MatTableDataSource<any>;
// Is optional, if id col is known this will improve performance.
@Input('shiftClickSourceId') idArr: string[];
@Input('shiftClickSelectModel') selection: SelectionModel<any>;
constructor(private host: ElementRef) {}
ngOnInit() {
// data can change order (sorting) and can be reduced (filtering)
this.alwaysTheRightSourceArray$ = this.dataSource.connect()
.pipe(
tap(_ => {
this.source = this.dataSource
.sortData(
this.dataSource.filteredData,
this.dataSource.sort
);
})
);
// lets record the last change of
this.recordSelectionChange$ = this.selection.changed.pipe(
shareReplay(1)
)
// clicks on tbody mean that we need to do something
this.host.nativeElement.childNodes[1].addEventListener("click", function() {
this.click$.next();
}.bind(this));
const reactOnClickOnTbody$ =
this.click$.pipe(
withLatestFrom(this.shiftHolding$.asObservable()),
withLatestFrom(this.recordSelectionChange$, (arr, c): [SelectionChange<any>, boolean ] => [c, arr[1]] ),
tap( arr => {
const v = arr[0];
const sh = arr[1];
const ans = [...v.added, ...v.removed][0];
if ( sh && this.lastItem ) {
this.onTbodyClicked(this.lastItem, ans);
} else {
this.lastItem = ans;
this.lastShiftSelection = undefined;
// console.log('clear');
}
}),
);
merge(
reactOnClickOnTbody$,
this.alwaysTheRightSourceArray$
).pipe(takeUntil(this.finishHim.asObservable()))
.subscribe();
}
// This function does all the real work.
onTbodyClicked(from: object, to: object) {
// console.log('onTbodyClickedRuns');
const fIdx = this.getIndexBasedOnData(from);
const tIdx = this.getIndexBasedOnData(to);
let ans;
let ans_last;
if( fIdx > tIdx ) {
ans = this.source.slice(tIdx, fIdx);
} else {
// console.log('seconds index case');
ans = this.source.slice(fIdx +1 , tIdx + 1);
}
if (this.lastShiftSelection) {
this.selection['_emitChanges']=false;
this.selection.deselect(...this.lastShiftSelection);
this.selection['_emitChanges']=true;
}
this.lastShiftSelection = [...ans];
if( fIdx > tIdx ) {
ans_last = ans.shift();
} else {
ans_last = ans.pop();
}
// console.log(this.lastShiftSelection);
const cond = ans.every(el => this.selection.isSelected(el)) && !this.selection.isSelected(ans_last)
if ( cond ) {
// console.log('deselect')
this.selection['_emitChanges']=false;
this.selection.deselect(...ans);
this.selection['_emitChanges']=true;
} else {
// console.log('select')
this.selection['_emitChanges']=false;
this.selection.select(...ans, ans_last);
this.selection['_emitChanges']=true;
}
}
// helper function
private getIndexBasedOnData(row, source = this.source): number {
let ind: number;
if (this.idArr) {
ind = source.findIndex( _d => this.idArr.every(id => _d[id] === row[id] ));
} else {
ind = source.findIndex( _d => Object.keys(row).every(k => _d[k] === row[k] ));
}
return ind;
}
ngOnDestroy() {
this.finishHim.next();
this.finishHim.complete();
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3709 次 |
| 最近记录: |