UNPKG

@tanstack/ember-table

Version:

Headless UI for building powerful tables & datagrids for Ember.

188 lines (134 loc) 7.46 kB
--- name: table-state description: > Read, track, initialize, control, and reset TanStack Ember Table v9 state through Glimmer-reactive table APIs, baseAtoms, atoms, store, Ember createAtom, or @tracked state plus on*Change. Load for ownership precedence, updater callbacks, stale template state, options-thunk reactivity, or incorrect table.Subscribe/store.subscribe usage. metadata: type: framework library: '@tanstack/ember-table' framework: ember library_version: '9.0.0-beta.78' requires: - '@tanstack/table-core#core' - getting-started sources: - 'TanStack/table:docs/framework/ember/guide/table-state.md' - 'TanStack/table:examples/ember/basic-external-atoms' - 'TanStack/table:examples/ember/basic-external-state' - 'TanStack/table:packages/ember-table/src/use-table.ts' - 'TanStack/table:packages/ember-table/src/reactivity.ts' - 'TanStack/table:packages/ember-table/src/signal.ts' --- This skill builds on `@tanstack/table-core#core` and `getting-started`. Keep state internal unless another part of the application must read, persist, validate, or drive a slice. ## State Surfaces State exists only for registered features: - `table.baseAtoms` are the table's internal writable atoms. - `table.atoms` are readonly, ownership-aware atoms for each registered slice. - `table.store.state` exposes the current flat state. - Feature methods such as `setSorting`, `setPageSize`, and `row.toggleSelected()` are the preferred write path. Ember's reactivity feature bridges these reads into Glimmer tracking. There is no `table.Subscribe`, no selector argument to `useTable`, and no need to subscribe during rendering. Read the relevant API inside a template-consumed getter: ```gts get rows() { return this.table.getRowModel().rows } get pageIndex() { return this.table.atoms.pagination.get().pageIndex } ``` `table.store.subscribe` is not the Ember rendering mechanism. The adapter's store is pull-based; Glimmer tracks individual property and atom reads. `features` and `atoms` are construct-time inputs in this adapter. Create them once before `useTable`; rerunning the options thunk updates ordinary live options but does not swap the table's feature set or atom owners. ## Choose One Owner Per Slice For a slice such as pagination, choose one source of truth: 1. Internal `baseAtoms` by default. 2. `initialState.pagination` for an internally owned starting value. 3. An external writable atom in `atoms.pagination`. 4. A `@tracked` value in `state.pagination`, paired with `onPaginationChange`. Read precedence is `atoms[key]` over `state[key]` over the internal `baseAtoms[key]`. Do not configure multiple owners merely as fallbacks. ## Internal and Initial State With no `initialState`, `atoms`, `state`, or `on[State]Change`, Table owns registered state internally. Use `initialState` when only the starting/reset value differs: ```gts table = useTable(() => ({ features, columns, data: this.data, initialState: { pagination: { pageIndex: 0, pageSize: 25 }, }, })) ``` Changing `initialState` later does not reset existing state. Prefer feature reset methods such as `resetPagination()`; passing `true` requests that feature's blank/default state. Core `table.reset()` resets internal base atoms and is not the primary reset path for externally owned atoms. ## External Ember Atoms Use the adapter's re-exported `createAtom` when a slice should be shared or read directly outside Table. It satisfies the TanStack Store atom contract and its reads are Glimmer-reactive: ```gts import { createAtom, useTable, type PaginationState, } from '@tanstack/ember-table' paginationAtom = createAtom<PaginationState>({ pageIndex: 0, pageSize: 10, }) table = useTable(() => ({ features, columns, data: this.data, atoms: { pagination: this.paginationAtom }, })) get pagination() { return this.paginationAtom.get() } ``` Feature APIs write through the external atom, so do not add `onPaginationChange` for the same atom-owned slice. Write the external atom itself when using the low-level path. ## Controlled Tracked State Use `state` plus the matching callback when a `@tracked` property owns a slice. Resolve both raw values and updater functions: ```gts import { createPaginatedRowModel, rowPaginationFeature, tableFeatures, type PaginationState, } from '@tanstack/ember-table' const features = tableFeatures({ rowPaginationFeature, paginatedRowModel: createPaginatedRowModel(), }) @tracked pagination: PaginationState = { pageIndex: 0, pageSize: 10 } table = useTable(() => ({ features, columns, data: this.data, state: { pagination: this.pagination }, onPaginationChange: (updater) => { this.pagination = typeof updater === 'function' ? updater(this.pagination) : updater }, })) ``` The pagination feature owns the state and APIs; the row-model slot enables client-side pagination. For server-owned pagination, keep `rowPaginationFeature`, omit the client row-model slot, and configure the matching manual/server data options. The options thunk must read `this.pagination`; assigning the tracked field reruns the thunk and feeds the controlled value back into Table. V9 has no global `onStateChange` option. ## Low-Level Writes and Types Use a feature method first. If internally owned state truly needs a low-level update: ```ts this.table.baseAtoms.pagination.set((old) => ({ ...old, pageIndex: 0 })) ``` Do not write `table.atoms.pagination`; it is readonly and may point at an external owner. Infer the complete registered shape with `TableState<typeof features>` and use feature-specific types such as `PaginationState` or `SortingState` for owned fields. ## Common Mistakes ### CRITICAL Controlling a slice without writing callbacks back Wrong: ```gts state: { pagination: this.pagination }, onPaginationChange: () => {}, ``` Correct: apply the value-or-updater to the tracked owner. Otherwise the slice is frozen at the controlled value. ### HIGH Expecting a snapshot read or subscription component Wrong: invent `<table.Subscribe>`, call `table.store.subscribe(...)` for rendering, or cache `table.store.state.pagination` outside a tracked getter. Correct: read `table.atoms.pagination.get()`, `table.store.state.pagination`, or the relevant Table API inside a template-consumed getter. `table.getState()` is removed v8 API, not an Ember state-read alternative. ### HIGH Mixing atom and state ownership If both `atoms.pagination` and `state.pagination` are present, the atom wins. Choose one owner so resets and writes have an unambiguous destination. ### HIGH Mutating controlled objects in place Wrong: `this.pagination.pageIndex++`. Correct: assign a new tracked value or let `table.setPageIndex(...)` invoke the controlled updater. Glimmer and the options thunk need an observable owner update. ### MEDIUM Writing the internal base atom for an external owner When `atoms.pagination` owns the slice, changing `baseAtoms.pagination` does not replace the active value. Write the external atom or use the feature API. ## API Discovery Inspect `node_modules/@tanstack/ember-table/declarations/use-table.d.ts` for option/state precedence and the Glimmer bridge, `signal.d.ts` for `createAtom` behavior, and `reactivity.d.ts` for scheduling/tracking. Inspect the registered feature under `node_modules/@tanstack/table-core/dist/features/<feature>/` for its state type, update API, and reset semantics.