Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: improve types for row grouping #81

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -166,14 +166,14 @@ export class DataTableBodyCellComponent<TRow extends { level?: number } = any>
return this._column;
}

@Input() set row(row: RowOrGroup<TRow>) {
@Input() set row(row: TRow) {
this._row = row;
this.cellContext.row = row;
this.checkValueUpdates();
this.cd.markForCheck();
}

get row(): RowOrGroup<TRow> {
get row(): TRow {
return this._row;
}

Expand Down Expand Up @@ -298,7 +298,7 @@ export class DataTableBodyCellComponent<TRow extends { level?: number } = any>
private _isSelected: boolean;
private _sorts: SortPropDir[];
private _column: TableColumn;
private _row: RowOrGroup<TRow>;
private _row: TRow;
private _group: TRow[];
private _rowHeight: number;
private _rowIndex: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { GroupContext } from '../../types/public.types';
@Directive({
selector: '[ngx-datatable-group-header-template]'
})
export class DatatableGroupHeaderTemplateDirective<TRow> {
static ngTemplateContextGuard<TRow>(
directive: DatatableGroupHeaderTemplateDirective<TRow>,
export class DatatableGroupHeaderTemplateDirective {
static ngTemplateContextGuard(
directive: DatatableGroupHeaderTemplateDirective,
context: unknown
): context is GroupContext<TRow> {
): context is GroupContext {
return true;
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { ContentChild, Directive, EventEmitter, Input, Output, TemplateRef } from '@angular/core';
import { DatatableGroupHeaderTemplateDirective } from './body-group-header-template.directive';
import { GroupContext } from '../../types/public.types';
import { Group, GroupContext, GroupToggleEvents } from '../../types/public.types';

@Directive({ selector: 'ngx-datatable-group-header' })
export class DatatableGroupHeaderDirective<TRow = any> {
/**
* Row height is required when virtual scroll is enabled.
*/
@Input() rowHeight: number | ((group?: any, index?: number) => number) = 0;
@Input() rowHeight: number | ((group?: Group<TRow>, index?: number) => number) = 0;

/**
* Show checkbox at group header to select all rows of the group.
Expand All @@ -27,12 +27,12 @@ export class DatatableGroupHeaderDirective<TRow = any> {
/**
* Track toggling of group visibility
*/
@Output() toggle: EventEmitter<any> = new EventEmitter();
@Output() toggle: EventEmitter<GroupToggleEvents<TRow>> = new EventEmitter();

/**
* Toggle the expansion of a group
*/
toggleExpandGroup(group: any): void {
toggleExpandGroup(group: Group<TRow>): void {
this.toggle.emit({
type: 'group',
value: group
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,7 @@ export class DataTableRowWrapperComponent<TRow = any> implements DoCheck, OnInit

@Input() set rowIndex(val: number) {
this._rowIndex = val;
this.rowContext.rowIndex = val;
this.groupContext.rowIndex = val;
(this.rowContext ?? this.groupContext).rowIndex = val;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should merge group and row context types and also merge the two properties.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am against merging. Those types just share two properties. The others are different.

export interface GroupContext<TRow> {
  group: Group<TRow>;
  expanded: boolean;
  rowIndex: number;
}

export interface RowDetailContext<TRow> {
  row: TRow;
  expanded: boolean;
  rowIndex: number;
  disableRow$?: Observable<boolean>;
}

They are emitted by different directives (DatatableGroupHeaderTemplateDirective and DatatableRowDetailTemplateDirective) so merging them would make them bad as we have properties in there which are never used in certain context.

Sure, the code looks bad this way, but we need to find a better solution in another MR. Merging the interface is far more worse in my opinion as this has a negative impact on consumer.

this.cd.markForCheck();
}

Expand All @@ -107,7 +106,7 @@ export class DataTableRowWrapperComponent<TRow = any> implements DoCheck, OnInit

@Input() set expanded(val: boolean) {
this._expanded = val;
this.groupContext.expanded = val;
(this.groupContext ?? this.rowContext)!.expanded = val;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merge into one property if possible and make it protected.

this.rowContext.expanded = val;
this.cd.markForCheck();
}
Expand All @@ -116,8 +115,8 @@ export class DataTableRowWrapperComponent<TRow = any> implements DoCheck, OnInit
return this._expanded;
}

groupContext: GroupContext<TRow>;
rowContext: RowDetailContext<TRow>;
groupContext?: GroupContext<TRow>;
rowContext?: RowDetailContext<TRow>;
Comment on lines +118 to +119
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merge to one property

disable$: BehaviorSubject<boolean>;

private rowDiffer: KeyValueDiffer<keyof RowOrGroup<TRow>, any>;
Expand All @@ -131,18 +130,21 @@ export class DataTableRowWrapperComponent<TRow = any> implements DoCheck, OnInit
differs: KeyValueDiffers,
private iterableDiffers: IterableDiffers
) {
this.groupContext = {
group: this.row,
expanded: this.expanded,
rowIndex: this.rowIndex
};

this.rowContext = {
row: this.row,
expanded: this.expanded,
rowIndex: this.rowIndex,
disableRow$: this.disable$
};
// this component renders either a group header or a row. Never both.
if (this.isGroup(this.row)) {
this.groupContext = {
group: this.row,
expanded: this.expanded,
rowIndex: this.rowIndex
};
} else {
this.rowContext = {
row: this.row,
expanded: this.expanded,
rowIndex: this.rowIndex,
disableRow$: this.disable$
};
}

this.rowDiffer = differs.find({}).create();
this.selectedRowsDiffer = this.iterableDiffers.find(this.selected ?? []).create();
Expand Down Expand Up @@ -174,8 +176,11 @@ export class DataTableRowWrapperComponent<TRow = any> implements DoCheck, OnInit
}

if (this.rowDiffer.diff(this.row)) {
this.rowContext.row = this.row;
this.groupContext.group = this.row;
if (this.isGroup(this.row)) {
this.groupContext.group = this.row;
} else {
this.rowContext.row = this.row;
}
this.cd.markForCheck();
}
// When groupheader is used with chechbox we use iterableDiffer
Expand Down Expand Up @@ -222,4 +227,8 @@ export class DataTableRowWrapperComponent<TRow = any> implements DoCheck, OnInit
selected: this.selected
});
}

isGroup(row: RowOrGroup<TRow>): row is Group<TRow> {
return !!this.groupHeader;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export class DataTableBodyRowComponent<TRow = any> implements DoCheck, OnChanges

@Input() expanded: boolean;
@Input() rowClass?: (row: RowOrGroup<TRow>) => string | Record<string, boolean>;
@Input() row: RowOrGroup<TRow>;
@Input() row: TRow;
@Input() group: TRow[];
@Input() isSelected: boolean;
@Input() rowIndex: number;
Expand Down
37 changes: 19 additions & 18 deletions projects/ngx-datatable/src/lib/components/body/body.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ import { ColumnGroupWidth } from '../../types/internal.types';
import {
DragEventData,
Group,
GroupToggleEvent,
GroupToggleEvents,
RowOrGroup,
RowToggleEvent,
RowToggleEvents,
ScrollEvent,
SelectionType,
TreeStatus
Expand Down Expand Up @@ -144,6 +148,7 @@ import {
</ng-template>

<ng-container *ngIf="isGroup(group)">
<!-- The row typecast is due to angular compiler acting weird. It is obvious that it is of type TRow, but the compiler does not understand. -->
<datatable-body-row
role="row"
[disable$]="rowWrapper.disable$"
Expand All @@ -155,7 +160,7 @@ import {
[offsetX]="offsetX"
[columns]="columns"
[rowHeight]="getRowHeight(row)"
[row]="row"
[row]="$any(row)"
[group]="group.value"
[rowIndex]="getRowIndex(row)"
[expanded]="getRowExpanded(row)"
Expand Down Expand Up @@ -415,28 +420,24 @@ export class DataTableBodyComponent<TRow extends { treeStatus?: TreeStatus } = a
*/
ngOnInit(): void {
if (this.rowDetail) {
this.listener = this.rowDetail.toggle.subscribe(
({ type, value }: { type: string; value: any }) => this.toggleStateChange(type, value)
);
this.listener = this.rowDetail.toggle.subscribe(event => this.toggleStateChange(event));
}

if (this.groupHeader) {
this.listener = this.groupHeader.toggle.subscribe(
({ type, value }: { type: string; value: any }) => {
// Remove default expansion state once user starts manual toggle.
this.groupExpansionDefault = false;
this.toggleStateChange(type, value);
}
);
this.listener = this.groupHeader.toggle.subscribe(event => {
// Remove default expansion state once user starts manual toggle.
this.groupExpansionDefault = false;
this.toggleStateChange(event);
});
}
}

private toggleStateChange(type: string, value: any) {
if (type === 'group' || type === 'row') {
this.toggleRowExpansion(value);
private toggleStateChange(event: RowToggleEvents<TRow> | GroupToggleEvents<TRow>) {
if (event.type === 'group' || event.type === 'row') {
this.toggleRowExpansion(event.value);
}
if (type === 'all') {
this.toggleAllRows(value);
if (event.type === 'all') {
this.toggleAllRows(event.value);
}

// Refresh rows after toggle
Expand Down Expand Up @@ -635,7 +636,7 @@ export class DataTableBodyComponent<TRow extends { treeStatus?: TreeStatus } = a
/**
* Get the height of the detail row.
*/
getDetailRowHeight = (row?: TRow, index?: number): number => {
getDetailRowHeight = (row?: RowOrGroup<TRow>, index?: number): number => {
if (!this.rowDetail) {
return 0;
}
Expand Down Expand Up @@ -827,7 +828,7 @@ export class DataTableBodyComponent<TRow extends { treeStatus?: TreeStatus } = a
* a part of the row object itself as we have to preserve the expanded row
* status in case of sorting and filtering of the row set.
*/
toggleRowExpansion(row: TRow): void {
toggleRowExpansion(row: RowOrGroup<TRow>): void {
// Capture the row index of the first row that is visible on the viewport.
const viewPortFirstRowIndex = this.getAdjustedViewPortIndex();
const rowExpandedIdx = this.getRowExpandedIdx(row, this.rowExpansions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,7 @@ export class DatatableComponent<TRow = any>
* if described in your markup.
*/
@ContentChildren(DataTableColumnDirective)
columnTemplates!: QueryList<DataTableColumnDirective>;
columnTemplates!: QueryList<DataTableColumnDirective>;

/**
* Row Detail templates gathered from the ContentChild
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { RowDetailContext } from '../../types/public.types';
@Directive({
selector: '[ngx-datatable-row-detail-template]'
})
export class DatatableRowDetailTemplateDirective<TRow = any> {
static ngTemplateContextGuard<TRow>(
directive: DatatableRowDetailTemplateDirective<TRow>,
export class DatatableRowDetailTemplateDirective {
static ngTemplateContextGuard(
directive: DatatableRowDetailTemplateDirective,
context: unknown
): context is RowDetailContext<TRow> {
): context is RowDetailContext {
return true;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ContentChild, Directive, EventEmitter, Input, Output, TemplateRef } from '@angular/core';
import { DatatableRowDetailTemplateDirective } from './row-detail-template.directive';
import { RowDetailContext } from '../../types/public.types';
import { RowDetailContext, RowToggleEvents } from '../../types/public.types';

@Directive({ selector: 'ngx-datatable-row-detail' })
export class DatatableRowDetailDirective<TRow = any> {
Expand All @@ -23,7 +23,7 @@ export class DatatableRowDetailDirective<TRow = any> {
/**
* Row detail row visbility was toggled.
*/
@Output() toggle: EventEmitter<any> = new EventEmitter();
@Output() toggle: EventEmitter<RowToggleEvents<TRow>> = new EventEmitter();

/**
* Toggle the expansion of the row
Expand Down
31 changes: 25 additions & 6 deletions projects/ngx-datatable/src/lib/types/public.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,16 @@ export interface HeaderCellContext {
selectFn: () => void;
}

export interface GroupContext<TRow> {
group: RowOrGroup<TRow>;
export interface GroupContext<TRow = any> {
group: Group<TRow>;
expanded: boolean;
rowIndex: number;
}

export interface CellContext<TRow> {
export interface CellContext<TRow = any> {
onCheckboxChangeFn: (event: Event) => void;
activateFn: (event: ActivateEvent<TRow>) => void;
row: RowOrGroup<TRow>;
row: TRow;
group: TRow[];
value: any;
column: TableColumn;
Expand Down Expand Up @@ -100,8 +100,8 @@ export interface Group<TRow> {
/** Type for either a row or a group */
export type RowOrGroup<TRow> = TRow | Group<TRow>;

export interface RowDetailContext<TRow> {
row: RowOrGroup<TRow>;
export interface RowDetailContext<TRow = any> {
row: TRow;
expanded: boolean;
rowIndex: number;
disableRow$?: Observable<boolean>;
Expand Down Expand Up @@ -135,6 +135,25 @@ export interface ScrollEvent {
offsetX: number;
}

export interface GroupToggleEvent<TRow> {
type: 'group';
value: Group<TRow>;
}

export interface RowToggleEvent<TRow> {
type: 'row';
value: TRow;
}

export interface AllToggleEvent {
type: 'all';
value: boolean;
}

export type GroupToggleEvents<TRow> = GroupToggleEvent<TRow> | AllToggleEvent;

export type RowToggleEvents<TRow> = RowToggleEvent<TRow> | AllToggleEvent;

export enum SelectionType {
single = 'single',
multi = 'multi',
Expand Down
8 changes: 5 additions & 3 deletions src/app/basic/row-grouping.component.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { Component, ViewChild } from '@angular/core';
import { GroupedEmployee } from '../data.model';
import {
ColumnMode,
DatatableComponent,
Group,
GroupToggleEvents,
SelectionType
} from 'projects/ngx-datatable/src/public-api';
import { GroupedEmployee } from '../data.model';

@Component({
selector: 'row-grouping-demo',
Expand Down Expand Up @@ -287,12 +289,12 @@ export class RowGroupingComponent {
this.rows = [...this.rows];
}

toggleExpandGroup(group) {
toggleExpandGroup(group: Group<GroupedEmployee>) {
console.log('Toggled Expand Group!', group);
this.table.groupHeader.toggleExpandGroup(group);
}

onDetailToggle(event) {
onDetailToggle(event: GroupToggleEvents<GroupedEmployee>) {
console.log('Detail Toggled', event);
}
}