From 4182c74d1090d2b62a0cbc8dfd41101ceac415b9 Mon Sep 17 00:00:00 2001 From: riccardoperra Date: Sun, 12 May 2024 11:52:17 +0200 Subject: [PATCH] update angular table state docs --- docs/framework/angular/guide/table-state.md | 211 +++++++++++--------- 1 file changed, 117 insertions(+), 94 deletions(-) diff --git a/docs/framework/angular/guide/table-state.md b/docs/framework/angular/guide/table-state.md index ac7cb017cc..bfdcdb4320 100644 --- a/docs/framework/angular/guide/table-state.md +++ b/docs/framework/angular/guide/table-state.md @@ -62,51 +62,62 @@ In order to control a particular state, you need to both pass in the correspondi Let's take filtering, sorting, and pagination as an example in a "manual" server-side data fetching scenario. You can store the filtering, sorting, and pagination state in your own state management, but leave out any other state like column order, column visibility, etc. if your API does not care about those values. -```jsx -readonly columnFilters = Angular.signal([]) //no default filters -readonly sorting = Angular.signal([ - { - id: 'age', - desc: true, //sort by age in descending order by default - } -]) -readonly pagination = Angular.signal({ - pageIndex: 0, - pageSize: 15 -}) - -//Use our controlled state values to fetch data -tableQuery = injectQuery(() => ({ - queryKey: ['users', columnFilters.value, sorting.value, pagination.value], - queryFn: () => fetchUsers(columnFilters.value, sorting.value, pagination.value), - //... -})) +```ts +import {signal} from '@angular/core'; +import {SortingState, ColumnFiltersState, PaginationState} from '@tanstack/angular-table' +import {toObservable} from "@angular/core/rxjs-interop"; +import {combineLatest, switchMap} from 'rxjs'; + +class TableComponent { + readonly columnFilters = signal([]) //no default filters + readonly sorting = signal([ + { + id: 'age', + desc: true, //sort by age in descending order by default + } + ]) + readonly pagination = signal({ + pageIndex: 0, + pageSize: 15 + }) + + //Use our controlled state values to fetch data + readonly data$ = combineLatest({ + filters: toObservable(this.columnFilters), + sorting: toObservable(this.sorting), + pagination: toObservable(this.pagination) + }).pipe( + switchMap(({filters, sorting, pagination}) => fetchData(filters, sorting, pagination)) + ) + readonly data = toSignal(this.data$); + + readonly table = createAngularTable(() => ({ + columns: this.columns, + data: this.data(), + //... + state: { + columnFilters: this.columnFilters(), //pass controlled state back to the table (overrides internal state) + sorting: this.sorting(), + pagination: this.pagination(), + }, + onColumnFiltersChange: updater => { //hoist columnFilters state into our own state management + updater instanceof Function + ? this.columnFilters.update(updater) + : this.columnFilters.set(updater) + }, + onSortingChange: updater => { + updater instanceof Function + ? this.sorting.update(updater) + : this.sorting.set(updater) + }, + onPaginationChange: updater => { + updater instanceof Function + ? this.pagination.update(updater) + : this.pagination.set(updater) + }, + })) +} -table = createAngularTable(() => ({ - columns: this.columns, - data: tableQuery.data(), - //... - state: { - columnFilters: this.columnFilters(), //pass controlled state back to the table (overrides internal state) - sorting: this.sorting(), - pagination: this.pagination(), - }, - onColumnFiltersChange: updater => { //hoist columnFilters state into our own state management - updater instanceof Function - ? this.columnFilters.update(updater) - : this.columnFilters.set(updater) - }, - onSortingChange: updater => { - updater instanceof Function - ? this.sorting.update(updater) - : this.sorting.set(updater) - }, - onPaginationChange: updater => { - updater instanceof Function - ? this.pagination.update(updater) - : this.pagination.set(updater) - }, -})) //... ``` @@ -116,32 +127,40 @@ Alternatively, you can control the entire table state with the `onStateChange` t A couple of more tricks may be needed to make this work. If you use the `onStateChange` table option, the initial values of the `state` must be populated with all of the relevant state values for all of the features that you want to use. You can either manually type out all of the initial state values, or use the `table.setOptions` API in a special way as shown below. -```jsx -//create a table instance with default state values -initTable = createAngularTable(() => ({ - columns: this.columns, - data: this.data(), - //... Note: `state` values are NOT passed in yet -})) +```ts -sate = Angular.useSignal({ - ...initTable.initialState, //populate the initial state with all of the default state values from the table instance - pagination: { - pageIndex: 0, - pageSize: 15 //optionally customize the initial pagination state. +class TableComponent { + // create an empty table state, we'll override it later + readonly state = signal({} as TableState); + + // create a table instance with default state values + readonly table = createAngularTable(() => ({ + columns: this.columns, + data: this.data(), + // our fully controlled state overrides the internal state + state: this.state(), + onStateChange: updater => { + // any state changes will be pushed up to our own state management + this.state.set( + updater instanceof Function ? updater(this.state()) : updater + ) + } + })) + + constructor() { + // set the initial table state + this.state.set({ + // populate the initial state with all of the default state values + // from the table instance + ...this.table.initialState, + pagination: { + pageIndex: 0, + pageSize: 15, // optionally customize the initial pagination state. + }, + }) } -}) - -//Use the table.setOptions API to merge our fully controlled state onto the table instance -//TODO - how to do this in Angular class? -// table = initTable.setOptions(prev => ({ -// ...prev, //preserve any other options that we have set up above -// state: state.value, //our fully controlled state overrides the internal state -// onStateChange: updater => { -// state.value = updater instanceOf Function ? updater(state.value) : updater //any state changes will be pushed up to our own state management -// }, -// })) +} ``` ### On State Change Callbacks @@ -152,22 +171,24 @@ So far, we have seen the `on[State]Change` and `onStateChange` table options wor Specifying an `on[State]Change` callback tells the table instance that this will be a controlled state. If you do not specify the corresponding `state` value, that state will be "frozen" with its initial value. -```jsx -sorting = Angular.useSignal([]) -//... -table = createAngularTable(() => ({ - columns: this.columns, - data: this.data(), - //... - state: { - sorting: this.sorting(), //required because we are using `onSortingChange` - }, - onSortingChange: updater => { // makes the `state.sorting` controlled - updater instanceof Function - ? this.sorting.update(updater) - : this.sorting.set(updater) - } -})) +```ts +class TableComponent { + sorting = signal([]) + + table = createAngularTable(() => ({ + columns: this.columns, + data: this.data(), + //... + state: { + sorting: this.sorting(), // required because we are using `onSortingChange` + }, + onSortingChange: updater => { // makes the `state.sorting` controlled + updater instanceof Function + ? this.sorting.update(updater) + : this.sorting.set(updater) + } + })) +} ``` #### 2. **Updaters can either be raw values or callback functions**. @@ -176,19 +197,21 @@ The `on[State]Change` and `onStateChange` callbacks work exactly like the `setSt What implications does this have? It means that if you want to add in some extra logic in any of the `on[State]Change` callbacks, you can do so, but you need to check whether or not the new incoming updater value is a function or value. -This is why you will see the `updater instanceOf Function ? this.state.update(updater) : this.state.set(updater)` pattern in the examples above. This pattern checks if the updater is a function, and if it is, it calls the function with the previous state value to get the new state value. +This is why you will see the `updater instanceof Function ? this.state.update(updater) : this.state.set(updater)` pattern in the examples above. This pattern checks if the updater is a function, and if it is, it calls the function with the previous state value to get the new state value. ### State Types All complex states in TanStack Table have their own TypeScript types that you can import and use. This can be handy for ensuring that you are using the correct data structures and properties for the state values that you are controlling. -```tsx -import { createAngularTable, SortingState } from '@tanstack/angular-table' -//... -const sorting = Angular.useSignal([ - { - id: 'age', //you should get autocomplete for the `id` and `desc` properties - desc: true, - } -]) -``` \ No newline at end of file +```ts +import {createAngularTable, SortingState} from '@tanstack/angular-table' + +class TableComponent { + readonly sorting = signal([ + { + id: 'age', // you should get autocomplete for the `id` and `desc` properties + desc: true, + } + ]) +} +```