From db23cf48c571a20e91dbc376d96d03dca5852769 Mon Sep 17 00:00:00 2001 From: Mamerto Giango Date: Mon, 6 May 2024 00:52:24 +0800 Subject: [PATCH 1/4] feat(angular-table): improve adapter implementation (TanStack#5524) * Added optional injector for more flexibility * Replace runInInjectionContext with injector in effect --- .../angular/grouping/src/app/app.component.ts | 18 ++- packages/angular-table/src/index.ts | 125 +++++++----------- 2 files changed, 59 insertions(+), 84 deletions(-) diff --git a/examples/angular/grouping/src/app/app.component.ts b/examples/angular/grouping/src/app/app.component.ts index c4e711d994..1ffbd34334 100644 --- a/examples/angular/grouping/src/app/app.component.ts +++ b/examples/angular/grouping/src/app/app.component.ts @@ -3,19 +3,19 @@ import { ChangeDetectionStrategy, Component, computed, - effect, signal, } from '@angular/core' import { - createAngularTable, FlexRenderDirective, + GroupingState, + PaginationState, + Updater, + createAngularTable, getCoreRowModel, getExpandedRowModel, getFilteredRowModel, getGroupedRowModel, getPaginationRowModel, - GroupingState, - Updater, } from '@tanstack/angular-table' import { columns } from './columns' import { makeData } from './makeData' @@ -31,6 +31,7 @@ export class AppComponent { title = 'grouping' data = signal(makeData(10000)) grouping = signal([]) + pagination = signal({ pageIndex: 0, pageSize: 10 }) stringifiedGrouping = computed(() => JSON.stringify(this.grouping(), null, 2)) @@ -39,6 +40,7 @@ export class AppComponent { columns: columns, state: { grouping: this.grouping(), + pagination: this.pagination(), }, onGroupingChange: (updaterOrValue: Updater) => { const groupingState = @@ -47,6 +49,13 @@ export class AppComponent { : updaterOrValue this.grouping.set(groupingState) }, + onPaginationChange: (updaterOrValue: Updater) => { + const paginationState = + typeof updaterOrValue === 'function' + ? updaterOrValue({ ...this.pagination() }) + : updaterOrValue + this.pagination.set(paginationState) + }, getExpandedRowModel: getExpandedRowModel(), getGroupedRowModel: getGroupedRowModel(), getCoreRowModel: getCoreRowModel(), @@ -66,5 +75,6 @@ export class AppComponent { refreshData() { this.data.set(makeData(10000)) + this.pagination.set({ pageIndex: 0, pageSize: 10 }) } } diff --git a/packages/angular-table/src/index.ts b/packages/angular-table/src/index.ts index 20e2db99b3..af0a4a43aa 100644 --- a/packages/angular-table/src/index.ts +++ b/packages/angular-table/src/index.ts @@ -1,96 +1,61 @@ -import { - computed, - effect, - inject, - Injector, - runInInjectionContext, - type Signal, - signal, - untracked, -} from '@angular/core' +import { computed, effect, inject, Injector, signal } from '@angular/core' import { createTable, RowData, - type Table, TableOptions, TableOptionsResolved, + TableState, + type Table, } from '@tanstack/table-core' -import { proxifyTable } from './proxy' import { lazyInit } from './lazy-signal-initializer' export * from '@tanstack/table-core' -export { - FlexRenderDirective, - FlexRenderComponent, - injectFlexRenderContext, -} from './flex-render' +export { FlexRenderDirective } from './flex-render' export function createAngularTable( - options: () => TableOptions -): Table & Signal> { - const injector = inject(Injector) - - return lazyInit(() => - runInInjectionContext(injector, () => { - const resolvedOptionsSignal = computed>( - () => { - return { - state: {}, - onStateChange: () => {}, - renderFallbackValue: null, - ...options(), - } - } - ) - - const notifier = signal([], { equal: () => false }) - const table = createTable(untracked(resolvedOptionsSignal)) - const state = signal(table.initialState) - - function updateOptions() { - const tableState = untracked(state) - const resolvedOptions = untracked(resolvedOptionsSignal) - untracked(() => { - table.setOptions(prev => ({ - ...prev, - ...resolvedOptions, - state: { ...tableState, ...resolvedOptions.state }, - onStateChange: updater => { - const value = - updater instanceof Function ? updater(tableState) : updater - state.set(value) - resolvedOptions.onStateChange?.(updater) - }, - })) - }) - } - - updateOptions() + options: () => TableOptions, + injector?: Injector +): Table { + if (!injector) injector = inject(Injector) + + return lazyInit(() => { + // Compose table resolved options as computed. + // This will allow the effect to be triggered when options are updated. + const resolvedOptionsSignal = computed>(() => ({ + state: {}, + onStateChange: () => {}, + renderFallbackValue: null, + ...options(), + })) + + const table = createTable(resolvedOptionsSignal()) + + // By default, manage table state here using the table's initial state + const state = signal(table.initialState) + + function updateOptions() { + const tableState = state() + const resolvedOptions = resolvedOptionsSignal() + table.setOptions(prev => ({ + ...prev, + ...resolvedOptions, + state: { ...tableState, ...resolvedOptions.state }, + onStateChange: updater => { + const value = + updater instanceof Function ? updater(tableState) : updater + state.set(value) + resolvedOptions.onStateChange?.(updater) + }, + })) + } - let firstRender = true - effect(() => { - void [state(), resolvedOptionsSignal()] - if (firstRender) { - return (firstRender = false) - } - untracked(() => { - updateOptions() - notifier.set([]) - }) - }) + // set table options again when options are updated + effect(() => updateOptions(), { injector }) - const tableSignal = computed( - () => { - notifier() - return table - }, - { - equal: () => false, - } - ) + // set table options for the first time + updateOptions() - return proxifyTable(tableSignal) - }) - ) + return table + }) } From 363bac432c6a1364a8ba180d3ff76a601ed516e4 Mon Sep 17 00:00:00 2001 From: Mamerto Giango Date: Mon, 6 May 2024 21:04:31 +0800 Subject: [PATCH 2/4] feat(angular-table): Added proxifyTable back --- .../angular/grouping/src/app/app.component.ts | 11 ---------- packages/angular-table/src/index.ts | 21 +++++++++++++++++-- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/examples/angular/grouping/src/app/app.component.ts b/examples/angular/grouping/src/app/app.component.ts index 1ffbd34334..f7cc89c443 100644 --- a/examples/angular/grouping/src/app/app.component.ts +++ b/examples/angular/grouping/src/app/app.component.ts @@ -8,7 +8,6 @@ import { import { FlexRenderDirective, GroupingState, - PaginationState, Updater, createAngularTable, getCoreRowModel, @@ -31,7 +30,6 @@ export class AppComponent { title = 'grouping' data = signal(makeData(10000)) grouping = signal([]) - pagination = signal({ pageIndex: 0, pageSize: 10 }) stringifiedGrouping = computed(() => JSON.stringify(this.grouping(), null, 2)) @@ -40,7 +38,6 @@ export class AppComponent { columns: columns, state: { grouping: this.grouping(), - pagination: this.pagination(), }, onGroupingChange: (updaterOrValue: Updater) => { const groupingState = @@ -49,13 +46,6 @@ export class AppComponent { : updaterOrValue this.grouping.set(groupingState) }, - onPaginationChange: (updaterOrValue: Updater) => { - const paginationState = - typeof updaterOrValue === 'function' - ? updaterOrValue({ ...this.pagination() }) - : updaterOrValue - this.pagination.set(paginationState) - }, getExpandedRowModel: getExpandedRowModel(), getGroupedRowModel: getGroupedRowModel(), getCoreRowModel: getCoreRowModel(), @@ -75,6 +65,5 @@ export class AppComponent { refreshData() { this.data.set(makeData(10000)) - this.pagination.set({ pageIndex: 0, pageSize: 10 }) } } diff --git a/packages/angular-table/src/index.ts b/packages/angular-table/src/index.ts index af0a4a43aa..12d9402f92 100644 --- a/packages/angular-table/src/index.ts +++ b/packages/angular-table/src/index.ts @@ -8,10 +8,15 @@ import { type Table, } from '@tanstack/table-core' import { lazyInit } from './lazy-signal-initializer' +import { proxifyTable } from './proxy' export * from '@tanstack/table-core' -export { FlexRenderDirective } from './flex-render' +export { + FlexRenderComponent, + FlexRenderDirective, + injectFlexRenderContext, +} from './flex-render' export function createAngularTable( options: () => TableOptions, @@ -56,6 +61,18 @@ export function createAngularTable( // set table options for the first time updateOptions() - return table + // convert table instance to signal for proxify to listen to any table state and options changes + const tableSignal = computed( + () => { + void [state(), resolvedOptionsSignal()] + return table + }, + { + equal: () => false, + } + ) + + // proxify Table instance to provide ability for consumer to listen to any table state changes + return proxifyTable(tableSignal) }) } From 07c844085d68b1b99224efbd561b39cdbea4cbae Mon Sep 17 00:00:00 2001 From: Mamerto Giango Date: Tue, 7 May 2024 10:39:56 +0800 Subject: [PATCH 3/4] feat(angular-table): adding back notifier signal for table changed --- packages/angular-table/src/index.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/angular-table/src/index.ts b/packages/angular-table/src/index.ts index 12d9402f92..dba99f595b 100644 --- a/packages/angular-table/src/index.ts +++ b/packages/angular-table/src/index.ts @@ -55,8 +55,18 @@ export function createAngularTable( })) } + // notifier for tableSignal whenever `updateOptions` is invoked + // this to make sure that table options is set first before table + // instance change is propagated to consumer + const tableChangeNotifier = signal([], { equal: () => false }) // set table options again when options are updated - effect(() => updateOptions(), { injector }) + effect( + () => { + updateOptions() + tableChangeNotifier.set([]) + }, + { injector } + ) // set table options for the first time updateOptions() @@ -64,7 +74,7 @@ export function createAngularTable( // convert table instance to signal for proxify to listen to any table state and options changes const tableSignal = computed( () => { - void [state(), resolvedOptionsSignal()] + tableChangeNotifier() return table }, { From b16a1946efa7ed3fb4a0b2a7781a4c4f7f3e2526 Mon Sep 17 00:00:00 2001 From: Mamerto Giango Date: Tue, 7 May 2024 12:42:02 +0800 Subject: [PATCH 4/4] feat(angular-table): Improve logic in setting table options *set table options inside computed before returning the table instance *remove redundant signals and effect *remove injector as it no longer required *update Grouping example to show how to pass signal when creating table --- .../angular/grouping/src/app/app.component.ts | 4 +- packages/angular-table/src/index.ts | 54 +++++++------------ 2 files changed, 22 insertions(+), 36 deletions(-) diff --git a/examples/angular/grouping/src/app/app.component.ts b/examples/angular/grouping/src/app/app.component.ts index f7cc89c443..524f3c152f 100644 --- a/examples/angular/grouping/src/app/app.component.ts +++ b/examples/angular/grouping/src/app/app.component.ts @@ -33,7 +33,7 @@ export class AppComponent { stringifiedGrouping = computed(() => JSON.stringify(this.grouping(), null, 2)) - table = createAngularTable(() => ({ + tableOptions = computed(() => ({ data: this.data(), columns: columns, state: { @@ -54,6 +54,8 @@ export class AppComponent { debugTable: true, })) + table = createAngularTable(this.tableOptions) + onPageInputChange(event: any): void { const page = event.target.value ? Number(event.target.value) - 1 : 0 this.table.setPageIndex(page) diff --git a/packages/angular-table/src/index.ts b/packages/angular-table/src/index.ts index dba99f595b..b702caeb74 100644 --- a/packages/angular-table/src/index.ts +++ b/packages/angular-table/src/index.ts @@ -1,10 +1,10 @@ -import { computed, effect, inject, Injector, signal } from '@angular/core' +import { computed, signal } from '@angular/core' import { - createTable, RowData, TableOptions, TableOptionsResolved, TableState, + createTable, type Table, } from '@tanstack/table-core' import { lazyInit } from './lazy-signal-initializer' @@ -19,62 +19,46 @@ export { } from './flex-render' export function createAngularTable( - options: () => TableOptions, - injector?: Injector + options: () => TableOptions ): Table { - if (!injector) injector = inject(Injector) - return lazyInit(() => { - // Compose table resolved options as computed. - // This will allow the effect to be triggered when options are updated. - const resolvedOptionsSignal = computed>(() => ({ + const resolvedOptions = { state: {}, onStateChange: () => {}, renderFallbackValue: null, ...options(), - })) + } - const table = createTable(resolvedOptionsSignal()) + const table = createTable(resolvedOptions) // By default, manage table state here using the table's initial state const state = signal(table.initialState) - function updateOptions() { + // Compose table options using computed. + // This is to allow `tableSignal` to listen and set table option + const updatedOptions = computed>(() => { + // listen to table state changed const tableState = state() - const resolvedOptions = resolvedOptionsSignal() - table.setOptions(prev => ({ - ...prev, + // listen to input options changed + const tableOptions = options() + return { + ...table.options, ...resolvedOptions, - state: { ...tableState, ...resolvedOptions.state }, + ...tableOptions, + state: { ...tableState, ...tableOptions.state }, onStateChange: updater => { const value = updater instanceof Function ? updater(tableState) : updater state.set(value) resolvedOptions.onStateChange?.(updater) }, - })) - } - - // notifier for tableSignal whenever `updateOptions` is invoked - // this to make sure that table options is set first before table - // instance change is propagated to consumer - const tableChangeNotifier = signal([], { equal: () => false }) - // set table options again when options are updated - effect( - () => { - updateOptions() - tableChangeNotifier.set([]) - }, - { injector } - ) - - // set table options for the first time - updateOptions() + } + }) // convert table instance to signal for proxify to listen to any table state and options changes const tableSignal = computed( () => { - tableChangeNotifier() + table.setOptions(updatedOptions()) return table }, {