Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(angular-table): adds Angular adapter for tanstack/table (#5326) #5432

Merged
merged 45 commits into from
May 12, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
afa27ad
feat(angular-table): adds Angular adapter for tanstack/table (#5326)
jrgokavalsa Mar 25, 2024
54034ef
docs config cleanup
KevinVandy Mar 25, 2024
a36a507
Merge branch 'main' into feat-angular-table
KevinVandy Mar 26, 2024
f118589
Merge branch 'main' into feat-angular-table
KevinVandy Mar 26, 2024
361577e
feat: signal angular table adapter implementation
riccardoperra Mar 26, 2024
8516ea7
feat: table proxy detect memoized fns
riccardoperra Mar 26, 2024
9b195b3
fix proxy property returning value
riccardoperra Mar 26, 2024
bf32e66
feat: improve naming
riccardoperra Mar 26, 2024
129f921
save new reference of table signal on every update
riccardoperra Mar 27, 2024
3aab637
computed trap proxy for fns with 1 argument
riccardoperra Mar 27, 2024
d0bd1dc
Merge branch 'main' into feat-angular-table
KevinVandy Mar 28, 2024
6b5d921
Merge branch 'main' into feat-angular-table
KevinVandy Apr 1, 2024
5817368
Merge remote-tracking branch 'origin/main' into feat-angular-table
riccardoperra Apr 29, 2024
1aa5353
update pnpm-lock.yaml
riccardoperra Apr 29, 2024
c5b1e71
refactor proxy implementation
riccardoperra Apr 29, 2024
6df7cf6
fix dependencies
riccardoperra Apr 29, 2024
b450b3d
cleanup imports
riccardoperra Apr 29, 2024
071ce7a
refactor basic example
riccardoperra Apr 29, 2024
fd0ca50
fix build
riccardoperra Apr 29, 2024
8d6f123
run prettier
riccardoperra Apr 29, 2024
3015158
add grouping example, fix ci
riccardoperra May 1, 2024
213b7a6
add grouping example, fix ci
riccardoperra May 1, 2024
2ff60af
add row selection example
riccardoperra May 1, 2024
1cdec2a
add column visibility example
riccardoperra May 1, 2024
dd68ab3
update examples add signal input required example
riccardoperra May 2, 2024
a571cba
improve angular table impl, fix flex-render change detection issues
riccardoperra May 2, 2024
f651c6c
fix build
riccardoperra May 2, 2024
38879fd
Merge branch 'main' into feat-angular-table
KevinVandy May 3, 2024
d4c514a
feat(angular-table): improve adapter implementation (#5524)
riccardoperra May 4, 2024
67a0b10
example cleanup
KevinVandy May 5, 2024
ca9491c
update lock file
KevinVandy May 5, 2024
7af1fd6
feat(angular-table): added injector optional parameter for more flexi…
merto20 May 7, 2024
804a0d2
Merge branch 'main' into feat-angular-table
KevinVandy May 9, 2024
f567280
update angular adapter and state docs
KevinVandy May 9, 2024
df4f32b
prettier
KevinVandy May 9, 2024
1518ea8
Merge branch 'main' into feat-angular-table
KevinVandy May 10, 2024
8493be0
update docs config with angular examples
KevinVandy May 10, 2024
412c76c
update angular table state docs (#5545)
riccardoperra May 12, 2024
2c916d2
Angular examples and dependencies improvements (#5546)
arnoud-dv May 12, 2024
059c018
docs(angular-table): Add documentation for FlexRenderDirective (#5543)
riccardoperra May 12, 2024
abf605c
remove double build package.json from angular build
KevinVandy May 12, 2024
9984118
update link name
KevinVandy May 12, 2024
ea7da50
docs pass
KevinVandy May 12, 2024
4dc3d72
update esm exports in package.json
KevinVandy May 12, 2024
529bc29
update flexrender types
KevinVandy May 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,5 @@ yarn.lock
.nx/cache
vite.config.js.timestamp-*
vite.config.ts.timestamp-*

.angular
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@
**/docs
**/old-examples
pnpm-lock.yaml

.angular
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Try other [TanStack](https://tanstack.com) libraries:

You may know **TanStack Table** by our adapter names, too!

- [Angular Table](https://tanstack.com/table/v8/docs/adapters/angular-table)
- [Qwik Table](https://tanstack.com/table/v8/docs/adapters/qwik-table)
- [**React Table**](https://tanstack.com/table/v8/docs/adapters/react-table)
- [Solid Table](https://tanstack.com/table/v8/docs/adapters/solid-table)
Expand Down Expand Up @@ -115,6 +116,7 @@ Install one of the following packages based on your framework of choice:

```bash
# Npm
npm install @tanstack/angular-table
npm install @tanstack/qwik-table
npm install @tanstack/react-table
npm install @tanstack/solid-table
Expand Down
2 changes: 1 addition & 1 deletion docs/api/core/table.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Table APIs
---

## `useReactTable` / `createSolidTable` / `useQwikTable` / `useVueTable` / `createSvelteTable`
## `createAngularTable` / `useReactTable` / `createSolidTable` / `useQwikTable` / `useVueTable` / `createSvelteTable`

```tsx
type useReactTable = <TData extends AnyData>(
Expand Down
35 changes: 35 additions & 0 deletions docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@
}
],
"frameworks": [
{
"label": "angular",
"children": [
{
"label": "Angular Table",
"to": "framework/angular/angular-table"
}
]
},
{
"label": "qwik",
"children": [
Expand Down Expand Up @@ -116,6 +125,15 @@
}
],
"frameworks": [
{
"label": "angular",
"children": [
{
"label": "Table State",
"to": "framework/angular/guide/table-state"
}
]
},
{
"label": "qwik",
"children": [
Expand Down Expand Up @@ -336,6 +354,23 @@
"label": "Examples",
"children": [],
"frameworks": [
{
"label": "angular",
"children": [
{
"to": "framework/angular/examples/basic",
"label": "Basic"
},
{
"to": "framework/angular/examples/grouping",
"label": "Grouping"
},
{
"to": "framework/angular/examples/row-selection",
"label": "Row Selection"
}
]
},
{
"label": "qwik",
"children": [
Expand Down
59 changes: 59 additions & 0 deletions docs/framework/angular/angular-table.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
title: Angular Table
---

The `@tanstack/angular-table` adapter is a wrapper around the core table logic. Most of it's job is related to managing state the "angular" way, providing types and the rendering implementation of cell/header/footer templates.

## Exports

`@tanstack/angular-table` re-exports all of `@tanstack/table-core`'s and the following:

### `createAngularTable`

Takes an `options` object and returns a table.

```ts
import { createAngularTable } from '@tanstack/angular-table'

ngOnInit() {
this.table = createAngularTable(options)
}
// ...render your table in template

```

### `FlexRender`

A Angular component for rendering cell/header/footer templates with dynamic values.

Example:

```ts
@Component({
imports: [FlexRenderDirective],
//...
})
```

```angular2html
<tbody>
@for (row of table.getRowModel().rows; track row.id) {
<tr>
@for (cell of row.getVisibleCells(); track cell.id) {
<td>
<ng-container
*flexRender="
cell.column.columnDef.cell;
props: cell.getContext();
let cell
"
>
{{ cell }}
</ng-container>
</td>
}
</tr>
}
</tbody>
</template>
```
178 changes: 178 additions & 0 deletions docs/framework/angular/guide/table-state.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
---
title: Table State (Angular) Guide
---

## Table State (Angular) Guide

TanStack Table has a simple underlying internal state management system to store and manage the state of the table. It also lets you selectively pull out any state that you need to manage in your own state management. This guide will walk you through the different ways in which you can interact with and manage the state of the table.

### Accessing Table State

You do not need to set up anything special in order for the table state to work. If you pass nothing into either `state`, `initialState`, or any of the `on[State]Change` table options, the table will manage its own state internally. You can access any part of this internal state by using the `table.getState()` table instance API.

```ts
this.table = createAngularTable({
data: this.data,
columns: columns,
//...
})

console.log(this.table.getState()) //access the entire internal state
console.log(this.table.getState().rowSelection) //access just the row selection state
```

### Custom Initial State

If all you need to do for certain states is customize their initial default values, you still do not need to manage any of the state yourself. You can simply set values in the `initialState` option of the table instance.

```jsx
const table = useAngularTable({
columns,
data,
initialState: {
columnOrder: ['age', 'firstName', 'lastName'], //customize the initial column order
columnVisibility: {
id: false //hide the id column by default
},
expanded: true, //expand all rows by default
sorting: [
{
id: 'age',
desc: true //sort by age in descending order by default
}
]
},
//...
})
```

> **Note**: Only specify each particular state in either `initialState` or `state`, but not both. If you pass in a particular state value to both `initialState` and `state`, the initialized state in `state` will take overwrite any corresponding value in `initialState`.

### Controlled State

If you need easy access to the table state in other areas of your application, TanStack Table makes it easy to control and manage any or all of the table state in your own state management system. You can do this by passing in your own state and state management functions to the `state` and `on[State]Change` table options.

#### Individual Controlled State

You can control just the state that you need easy access to. You do NOT have to control all of the table state if you do not need to. It is recommended to only control the state that you need on a case-by-case basis.

In order to control a particular state, you need to both pass in the corresponding `state` value and the `on[State]Change` function to the table instance.

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
const columnFilters = Angular.useSignal([]) //no default filters
const sorting = Angular.useSignal([{
id: 'age',
desc: true, //sort by age in descending order by default
}])
const pagination = Angular.useSignal({ pageIndex: 0, pageSize: 15 })

//Use our controlled state values to fetch data
const tableQuery = useQuery({
queryKey: ['users', columnFilters.value, sorting.value, pagination.value],
queryFn: () => fetchUsers(columnFilters.value, sorting.value, pagination.value),
//...
})

const table = useAngularTable({
columns: columns.value,
data: tableQuery.data,
//...
state: {
columnFilters: columnFilters.value, //pass controlled state back to the table (overrides internal state)
sorting: sorting.value,
pagination: pagination.value,
},
onColumnFiltersChange: updater => {
columnFilters.value = updater instanceOf Function ? updater(columnFilters.value) : updater //hoist columnFilters state into our own state management
},
onSortingChange: updater => {
sorting.value = updater instanceOf Function ? updater(sorting.value) : updater
},
onPaginationChange: updater => {
pagination.value = updater instanceOf Function ? updater(pagination.value) : updater
},
})
//...
```

#### Fully Controlled State

Alternatively, you can control the entire table state with the `onStateChange` table option. It will hoist out the entire table state into your own state management system. Be careful with this approach, as you might find that raising some frequently changing state values up a component tree, like `columnSizingInfo` state`, might cause bad performance issues.

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
const table = useAngularTable({
columns,
data,
//... Note: `state` values are NOT passed in yet
})


const sate = Angular.useSignal({
...table.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.
}
})

//Use the table.setOptions API to merge our fully controlled state onto the table instance
table.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

So far, we have seen the `on[State]Change` and `onStateChange` table options work to "hoist" the table state changes into our own state management. However, there are a few things about these using these options that you should be aware of.

#### 1. **State Change Callbacks MUST have their corresponding state value in the `state` option**.

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
const sorting = Angular.useSignal([])
//...
const table = useAngularTable({
columns,
data,
//...
state: {
sorting: sorting.value, //required because we are using `onSortingChange`
},
onSortingChange: updater => {
sorting.value = updater instanceOf Function ? updater(sorting) : updater //makes the `state.sorting` controlled
},
})
```

#### 2. **Updaters can either be raw values or callback functions**.

The `on[State]Change` and `onStateChange` callbacks work exactly like the `setState` functions in React. The updater values can either be a new state value or a callback function that takes the previous state value and returns the new state value.

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 ? updater(state.value) : 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 { useAngularTable, SortingState } from '@tanstack/angular-table'
//...
const sorting = Angular.useSignal<SortingState[]>([
{
id: 'age', //you should get autocomplete for the `id` and `desc` properties
desc: true,
}
])
```
5 changes: 4 additions & 1 deletion docs/guide/tables.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ title: Table Instance Guide

## Table Instance Guide

TanStack Table is a headless UI library. When we talk about the `table` or "table instance", we're not talking about a literal `<table>` element. Instead, we're referring to the core table object that contains the table state and APIs. The `table` instance is created by calling your adapter's `createTable` function (e.g. `useReactTable`, `createSolidTable`, `createSvelteTable`, `useQwikTable`, `useVueTable`).
TanStack Table is a headless UI library. When we talk about the `table` or "table instance", we're not talking about a literal `<table>` element. Instead, we're referring to the core table object that contains the table state and APIs. The `table` instance is created by calling your adapter's `createTable` function (e.g. `createAngularTable`, `useReactTable`, `createSolidTable`, `createSvelteTable`, `useQwikTable`, `useVueTable`).

### Creating a Table Instance

Expand Down Expand Up @@ -89,6 +89,9 @@ With our `columns` and `data` defined, we can now create our basic table instanc
//vanilla js
const table = createTable({ columns, data })

//angular
this.table = createAngularTable({ data: this.columns, data: this.data })

//react
const table = useReactTable({ columns, data })

Expand Down
6 changes: 6 additions & 0 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ Before we dig in to the API, let's get you set up!

Install your table adapter as a dependency using your favorite npm package manager

## Angular Table

```bash
npm install @tanstack/angular-table
```

## Qwik Table

```bash
Expand Down
2 changes: 1 addition & 1 deletion docs/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ While TanStack Table is written in [TypeScript](https://www.typescriptlang.org/)

## Headless

As it was mentioned extensively in the [Intro](./guide/introduction) section, TanStack Table is **headless**. This means that it doesn't render any DOM elements, and instead relies on you, the UI/UX developer to provide the table's markup and styles. This is a great way to build a table that can be used in any UI framework, including React, Vue, Solid, Svelte, Qwik, and even JS-to-native platforms like React Native!
As it was mentioned extensively in the [Intro](./guide/introduction) section, TanStack Table is **headless**. This means that it doesn't render any DOM elements, and instead relies on you, the UI/UX developer to provide the table's markup and styles. This is a great way to build a table that can be used in any UI framework, including React, Vue, Solid, Svelte, Qwik, Angular, and even JS-to-native platforms like React Native!

## Core Objects and Types

Expand Down
16 changes: 16 additions & 0 deletions examples/angular/basic/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Editor configuration, see https://editorconfig.org
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

[*.ts]
quote_type = single

[*.md]
max_line_length = off
trim_trailing_whitespace = false