Skip to content

Commit

Permalink
feat(ngrid): 3rd party storage for context api (#132)
Browse files Browse the repository at this point in the history
feat(ngrid/detail-row): integrate detail row state with context (#10)
fix(ngrid/detail-row): sort doesn't work well with detail row (#127)

Breaking Change
Default `whenContextChange` is now `context`
  • Loading branch information
shlomiassaf committed Dec 3, 2020
1 parent 4882e4a commit 0c9ca4c
Show file tree
Hide file tree
Showing 11 changed files with 105 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,15 @@ It might also occur when the data is updated due to custom refresh calls on the

The default behavior is such case is to re-render the detail row with the data of the new row but you can control this behavior using the input `whenContextChange` with 3 options:

- **context**: use the context to determine if to open or close the detail row
- **ignore**: don't do anything, leave as is (for manual intervention)
- **close**: close the detail row
- **render**: re-render the row with the new context (default)

Usually, what you will want is **context** (the default) which will remember the last state of the row and open it based on it.

W> Note that for "context" to work you need to use a datasource in client side mode and it must have a primary/identity column (pIndex) or it will not be able to identify the rows.

<div pbl-example-view="pbl-multi-page-example"></div>

### Custom row updates
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ <h1>Detail Row</h1>
<mat-form-field>
<mat-select placeholder="whenContextChange" [value]="detailRow?.whenContextChange" (selectionChange)="detailRow.whenContextChange = $event.value">
<mat-option value="ignore">Ignore</mat-option>
<mat-option value="context">Context</mat-option>
<mat-option value="render">Render</mat-option>
<mat-option value="close">Close</mat-option>
</mat-select>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export class MultiPageExample {
.default({minWidth: 100})
.table(
{ prop: 'detailRowHandle', label: ' ', type: 'detailRowHandle', minWidth: 48, maxWidth: 48 },
{ prop: 'id', sort: true, width: '40px' },
{ prop: 'id', pIndex: true, sort: true, width: '40px' },
{ prop: 'name', sort: true },
{ prop: 'gender', width: '50px' },
{ prop: 'birthdate', type: 'date' }
Expand Down
11 changes: 8 additions & 3 deletions libs/ngrid/detail-row/src/lib/detail-row/detail-row-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,14 @@ export class PblNgridDetailRowPluginDirective<T> implements OnDestroy {
@Input() excludeToggleFrom: string[];

/**
* Set the behavior when the row's context is changed while the detail row is opened (another row is displayed in place of the current row).
* Set the behavior when the row's context is changed while the detail row is opened (another row is displayed in place of the current row) or closed.
*
* - context: use the context to determine if to open or close the detail row
* - ignore: don't do anything, leave as is (for manual intervention)
* - close: close the detail row
* - render: re-render the row with the new context
*
* The default behavior is `render`
* The default behavior is `context`
*
* This scenario will pop-up when using pagination and the user move between pages or change the page size.
* It might also happen when the data is updated due to custom refresh calls on the datasource or any other scenario that might invoke a datasource update.
Expand All @@ -100,9 +101,13 @@ export class PblNgridDetailRowPluginDirective<T> implements OnDestroy {
* toggle the row (mimic `close`) or update the context manually. For example, if toggling open the detail row invokes a "fetch" operation that retrieves data for the detail row
* this will allow updates on context change.
*
* Usually, what you will want is "context" (the default) which will remember the last state of the row and open it based on it.
*
* > Note that for "context" to work you need to use a datasource in client side mode and it must have a primary/identity column (pIndex) or it will not be able to identify the rows.
*
* > Note that `toggledRowContextChange` fires regardless of the value set in `whenContextChange`
*/
@Input() whenContextChange: 'ignore' | 'close' | 'render' = 'render';
@Input() whenContextChange: 'ignore' | 'close' | 'render' | 'context' = 'context';

/**
* Emits whenever a detail row instance is toggled on/off
Expand Down
30 changes: 25 additions & 5 deletions libs/ngrid/detail-row/src/lib/detail-row/row.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ import { CdkRow } from '@angular/cdk/table';
import { PblNgridRowComponent, utils, PBL_NGRID_ROW_TEMPLATE, PblNgridComponent } from '@pebula/ngrid';
import { PblNgridDetailRowPluginDirective, PblDetailsRowToggleEvent, PLUGIN_KEY } from './detail-row-plugin';

declare module '@pebula/ngrid/lib/grid/context/types' {
interface ExternalRowContextState {
detailRow: boolean;
}
}

@Component({
selector: 'pbl-ngrid-row[detailRow]',
exportAs: 'pblNgridDetailRow',
Expand Down Expand Up @@ -41,6 +47,7 @@ export class PblNgridDetailRowComponent extends PblNgridRowComponent implements
private get _element(): HTMLElement { return this.el.nativeElement; }
private opened = false;
private plugin: PblNgridDetailRowPluginDirective<any>;
private prevIdentity: any;

constructor(@Optional() grid: PblNgridComponent,
el: ElementRef<HTMLElement>,
Expand All @@ -55,14 +62,26 @@ export class PblNgridDetailRowComponent extends PblNgridRowComponent implements
}

updateRow(): void {
const prevIdentity = this.context && this.context.$implicit;
const prevIdentity = this.prevIdentity;
super.updateRow();
if (this.opened) {
const currIdentity = this.context && this.context.$implicit;
if (currIdentity !== prevIdentity && currIdentity) {
this.prevIdentity = this.context?.identity;
if (this.plugin?.whenContextChange === 'context') {
if (this.context.getExternal('detailRow')) {
if (this.opened) {
this.render();
} else {
this.toggle(true);
}
} else {
if (this.opened) {
this.toggle(false);
}
}
} else if (this.opened) {
if (this.prevIdentity !== prevIdentity && this.prevIdentity) {
switch (this.plugin.whenContextChange) {
case 'render':
this.render();
this.render();
break;
case 'close':
this.toggle(false);
Expand All @@ -87,6 +106,7 @@ export class PblNgridDetailRowComponent extends PblNgridRowComponent implements
this._element.classList.add('pbl-row-detail-opened');
}

this.context.setExternal('detailRow', this.opened, true);
this.plugin.detailRowToggled(this.createEvent());
}
}
Expand Down
6 changes: 6 additions & 0 deletions libs/ngrid/src/lib/grid/context/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,12 @@ export class ContextApi<T = any> {
}
}

saveState(context: PblNgridRowContext<T>) {
if (context instanceof PblRowContext) {
this.cache.set(context.identity, context.getState());
}
}

getRow(row: number | HTMLElement): PblNgridRowContext<T> | undefined {
const index = typeof row === 'number' ? row : findRowRenderedIndex(row);
return this.rowContext(index);
Expand Down
18 changes: 16 additions & 2 deletions libs/ngrid/src/lib/grid/context/cell.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { PblNgridExtensionApi } from '../../ext/grid-ext-api';
import { PblNgridComponent } from '../ngrid.component';
import { CellContextState, PblNgridCellContext, PblNgridMetaCellContext, PblNgridRowContext } from './types';
import { CellContextState, ExternalCellContextState, PblNgridCellContext, PblNgridMetaCellContext, PblNgridRowContext } from './types';
import { PblColumn, PblMetaColumn } from '../column/model';
import { PblRowContext } from './row';

Expand Down Expand Up @@ -41,6 +41,7 @@ export class PblCellContext<T = any> implements PblNgridCellContext<T> {
private _editing = false;
private _focused = false;
private _selected = false;
private _external = {};

private _rowContext: PblRowContext<T>;
public col: PblColumn;
Expand Down Expand Up @@ -68,7 +69,7 @@ export class PblCellContext<T = any> implements PblNgridCellContext<T> {
}

static defaultState<T = any>(): CellContextState<T> {
return { editing: false, focused: false, selected: false };
return { editing: false, focused: false, selected: false, external: {} };
}

clone(col?: PblColumn): PblCellContext<T> {
Expand All @@ -77,11 +78,23 @@ export class PblCellContext<T = any> implements PblNgridCellContext<T> {
return ctx;
}

getExternal<P extends keyof ExternalCellContextState>(key: P): ExternalCellContextState[P] {
return this._external[key];
}

setExternal<P extends keyof ExternalCellContextState>(key: P, value: ExternalCellContextState[P], saveState = false) {
this._external[key] = value;
if (saveState) {
this._rowContext.saveState();
}
}

getState(): CellContextState<T> {
return {
editing: this._editing,
focused: this._focused,
selected: this._selected,
external: this._external,
};
}

Expand All @@ -92,6 +105,7 @@ export class PblCellContext<T = any> implements PblNgridCellContext<T> {
this._editing = state.editing;
this._focused = state.focused;
this._selected = state.selected;
this._external = state.external;

if (requiresReset) {
rowContext.updateCell(this);
Expand Down
22 changes: 20 additions & 2 deletions libs/ngrid/src/lib/grid/context/row.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { RowContext } from '@angular/cdk/table';
import { PblColumn } from '../column/model';
import { PblNgridExtensionApi } from '../../ext/grid-ext-api';
import { PblNgridComponent } from '../ngrid.component';
import { CellContextState, RowContextState, PblNgridRowContext } from './types';
import { CellContextState, RowContextState, PblNgridRowContext, ExternalRowContextState } from './types';
import { PblCellContext } from './cell';

declare module '@angular/cdk/table/row.d' {
Expand Down Expand Up @@ -39,6 +39,7 @@ export class PblRowContext<T> implements PblNgridRowContext<T> {
outOfView: boolean;

readonly grid: PblNgridComponent<T>;
private external: any = {};

/**
* Returns the length of cells context stored in this row
Expand Down Expand Up @@ -88,7 +89,18 @@ export class PblRowContext<T> implements PblNgridRowContext<T> {
for (let i = 0; i < cellsCount; i++) {
cells.push(PblCellContext.defaultState());
}
return { identity, dataIndex, cells, firstRender: true };
return { identity, dataIndex, cells, firstRender: true, external: {} };
}

getExternal<P extends keyof ExternalRowContextState>(key: P): ExternalRowContextState[P] {
return this.external[key];
}

setExternal<P extends keyof ExternalRowContextState>(key: P, value: ExternalRowContextState[P], saveState = false) {
this.external[key] = value;
if (saveState) {
this.saveState();
}
}

getState(): RowContextState<T> {
Expand All @@ -97,18 +109,24 @@ export class PblRowContext<T> implements PblNgridRowContext<T> {
dataIndex: this.dataIndex,
firstRender: this.firstRender,
cells: this.cells.map( c => c.getState() ),
external: this.external,
};
}

fromState(state: RowContextState<T>): void {
this.identity = state.identity;
this.firstRender = state.firstRender;
this.dataIndex = state.dataIndex;
this.external = state.external;
for (let i = 0, len = this.cells.length; i < len; i++) {
this.cells[i].fromState(state.cells[i], this);
}
}

saveState() {
this.extApi.contextApi.saveState(this);
}

updateContext(context: RowContext<T>): void {
context.dataIndex = this.dataIndex;
Object.assign(this, context);
Expand Down
21 changes: 16 additions & 5 deletions libs/ngrid/src/lib/grid/context/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,26 @@ declare module '@angular/cdk/table/table.d' {
}
}

export interface RowContextState<T = any> {
identity: any;
dataIndex: number;
cells: CellContextState<T>[];
firstRender: boolean;
external: any;
}

export interface CellContextState<T = any> {
editing: boolean;
focused: boolean;
selected: boolean;
external: any;
}

export interface RowContextState<T = any> {
identity: any;
dataIndex: number;
cells: CellContextState<T>[];
firstRender: boolean;
export interface ExternalRowContextState {

}
export interface ExternalCellContextState {

}

/**
Expand Down Expand Up @@ -187,6 +196,8 @@ export interface PblNgridContextApi<T = any> {
*/
clear(syncView?: boolean)

saveState(context: PblNgridRowContext<T>);

/**
* Try to find a specific row context, using the row identity, in the current view.
* If the row is not in the view (or even not in the cache) it will return undefined, otherwise returns the row's context instance (`PblRowContext`)
Expand Down
1 change: 1 addition & 0 deletions libs/ngrid/src/lib/grid/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export {
PblNgridContextApi,
PblRowContext,
CellReference, GridDataPoint,
ExternalRowContextState, ExternalCellContextState,
} from './context/index';

export { PblCdkTableComponent } from './pbl-cdk-table/pbl-cdk-table.component';
Expand Down
6 changes: 6 additions & 0 deletions libs/ngrid/src/lib/grid/ngrid.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,7 @@ export class PblNgridComponent<T = any> implements AfterContentInit, AfterViewIn

if ( processColumns === true ) {
this.invalidateColumns();
this.ngZone.onStable.pipe(take(1)).subscribe(() => this.rowsApi.syncRows('all', true));
}
}

Expand Down Expand Up @@ -571,6 +572,11 @@ export class PblNgridComponent<T = any> implements AfterContentInit, AfterViewIn
// UPDATE: This will not work because it will cause the width to be incorrect when used with vScrollNone
// TODO: Check why?
}

// We need to trigger CD when not using virtual scroll or else the rows won't show on initial loan, only after user interactions
if (!this.viewport.enabled) {
this.rowsApi.syncRows(true);
}
});
}
}
Expand Down

0 comments on commit 0c9ca4c

Please sign in to comment.