-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ngrid): cache when rendering rows
When scrolling with virtual scroll sometimes rows are added/removed at the edges, based on the size and position. This was expansive because it created and destroyed DOM elements. Now we cache these elements and their view's and just insert them when they are ready to join the grid again.
- Loading branch information
1 parent
35bbea8
commit 170c2d4
Showing
8 changed files
with
287 additions
and
104 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
74 changes: 0 additions & 74 deletions
74
libs/ngrid/src/lib/grid/pbl-cdk-table/bypass-cdk-cell rendering-repeater-strategy.ts
This file was deleted.
Oops, something went wrong.
122 changes: 122 additions & 0 deletions
122
libs/ngrid/src/lib/grid/pbl-cdk-table/ngrid-base-row-view-repeater-strategy.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import { EmbeddedViewRef, Inject, Injectable, IterableChangeRecord, IterableChanges, ViewContainerRef } from '@angular/core'; | ||
import { | ||
_ViewRepeater, | ||
_ViewRepeaterItemChange, | ||
_ViewRepeaterItemChanged, | ||
_ViewRepeaterItemContext, | ||
_ViewRepeaterItemContextFactory, | ||
_ViewRepeaterItemInsertArgs, | ||
_ViewRepeaterItemValueResolver, | ||
_ViewRepeaterOperation, | ||
} from '@angular/cdk/collections'; | ||
import { CdkRowDef, RenderRow, BaseRowDef, RowContext } from '@angular/cdk/table'; | ||
|
||
import { EXT_API_TOKEN, PblNgridInternalExtensionApi } from '../../ext/grid-ext-api'; | ||
import { PblRowContext } from '../context/row'; | ||
|
||
export interface BaseChangeOperationState<T, R extends RenderRow<T>, C extends PblRowContext<T>> { | ||
vcRef: ViewContainerRef; | ||
itemContextFactory: _ViewRepeaterItemContextFactory<T, R, C>; | ||
itemValueResolver: _ViewRepeaterItemValueResolver<T, R>; | ||
} | ||
|
||
export interface ChangeOperationState<T, R extends RenderRow<T>, C extends PblRowContext<T>> extends BaseChangeOperationState<T, R, C> { | ||
record: IterableChangeRecord<R>; | ||
view?: EmbeddedViewRef<C> | undefined; | ||
op?: _ViewRepeaterOperation; | ||
} | ||
|
||
@Injectable() | ||
export class PblNgridBaseRowViewRepeaterStrategy<T, R extends RenderRow<T>, C extends PblRowContext<T>> implements _ViewRepeater<T, R, C> { | ||
protected workaroundEnabled = false; | ||
protected renderer: { _renderCellTemplateForItem: (rowDef: BaseRowDef, context: RowContext<T>) => void; }; | ||
protected _cachedRenderDefMap = new Map<number, CdkRowDef<T>>(); | ||
|
||
constructor(@Inject(EXT_API_TOKEN) protected extApi: PblNgridInternalExtensionApi<T>) { | ||
extApi | ||
.onConstructed(() => { | ||
const cdkTable = extApi.cdkTable; | ||
this.renderer = cdkTable as any; | ||
this.workaroundEnabled = !cdkTable['_cachedRenderDefMap'] && typeof this.renderer._renderCellTemplateForItem === 'function'; | ||
}); | ||
} | ||
|
||
applyChanges(changes: IterableChanges<R>, | ||
vcRef: ViewContainerRef, | ||
itemContextFactory: _ViewRepeaterItemContextFactory<T, R, C>, | ||
itemValueResolver: _ViewRepeaterItemValueResolver<T, R>, | ||
itemViewChanged?: _ViewRepeaterItemChanged<R, C>) { | ||
const baseState: BaseChangeOperationState<T, R, C> = { | ||
vcRef, | ||
itemContextFactory: ( record: IterableChangeRecord<R>, | ||
adjustedPreviousIndex: number | null, | ||
currentIndex: number | null) => this.updateWithNgridContext(itemContextFactory(record, adjustedPreviousIndex, currentIndex)), | ||
itemValueResolver, | ||
}; | ||
changes.forEachOperation((record: IterableChangeRecord<R>, adjustedPreviousIndex: number | null, currentIndex: number | null) => { | ||
const state: ChangeOperationState<T, R, C> = Object.create(baseState); | ||
state.record = record; | ||
if (record.previousIndex == null) { | ||
this.addItem(adjustedPreviousIndex, currentIndex, state); | ||
} else if (currentIndex == null) { | ||
this.removeItem(adjustedPreviousIndex, state); | ||
} else { | ||
this.moveItem(adjustedPreviousIndex, currentIndex, state); | ||
} | ||
|
||
if (this.workaroundEnabled) { | ||
this.patch20765afterOp(state); | ||
} | ||
|
||
this.afterOperation(state); | ||
}); | ||
|
||
if (this.workaroundEnabled) { | ||
this.patch20765(vcRef, changes, itemContextFactory); | ||
} | ||
} | ||
|
||
detach(): void { } | ||
|
||
protected addItem(adjustedPreviousIndex: number | null, currentIndex: number | null, state: ChangeOperationState<T, R, C>) { } | ||
|
||
protected removeItem(removeAt: number, state: ChangeOperationState<T, R, C>) { } | ||
|
||
protected moveItem(moveFrom: number, moveTo: number, state: ChangeOperationState<T, R, C>) { } | ||
|
||
protected afterOperation(state: ChangeOperationState<T, R, C>) { } | ||
|
||
protected updateWithNgridContext(itemArgs: _ViewRepeaterItemInsertArgs<C>) { | ||
itemArgs.context = this.extApi.contextApi._createRowContext(itemArgs.context.$implicit, itemArgs.index) as any; | ||
return itemArgs; | ||
} | ||
|
||
// See https://github.com/angular/components/pull/20765 | ||
protected patch20765(viewContainerRef: ViewContainerRef, changes: IterableChanges<R>, itemContextFactory: _ViewRepeaterItemContextFactory<T, R, C>,) { | ||
changes.forEachIdentityChange = (fn: (record: IterableChangeRecord<R>) => void) => { | ||
changes.constructor.prototype.forEachIdentityChange.call(changes, (record: IterableChangeRecord<R>) => { | ||
fn(record); | ||
if (this._cachedRenderDefMap.get(record.currentIndex) !== record.item.rowDef) { | ||
viewContainerRef.remove(record.currentIndex); | ||
const insertContext = this.updateWithNgridContext(itemContextFactory(record, null, record.currentIndex)); | ||
viewContainerRef.createEmbeddedView(insertContext.templateRef, insertContext.context, insertContext.index); | ||
this._cachedRenderDefMap.set(record.currentIndex, record.item.rowDef); | ||
} | ||
}); | ||
} | ||
} | ||
|
||
protected patch20765afterOp(state: ChangeOperationState<T, R, C>) { | ||
switch (state.op) { | ||
case _ViewRepeaterOperation.REPLACED: | ||
case _ViewRepeaterOperation.INSERTED: | ||
case _ViewRepeaterOperation.MOVED: | ||
this._cachedRenderDefMap.set(state.record.currentIndex, state.record.item.rowDef); | ||
break; | ||
case _ViewRepeaterOperation.REMOVED: | ||
this._cachedRenderDefMap.delete(state.record.previousIndex); | ||
break; | ||
} | ||
} | ||
|
||
} |
Oops, something went wrong.