Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 196 additions & 0 deletions docs/framework/angular/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,200 @@ npm install @tanstack/angular-db

See the [Angular Functions Reference](../reference/index.md) to see the full list of functions available in the Angular Adapter.

For comprehensive documentation on writing queries (filtering, joins, aggregations, etc.), see the [Live Queries Guide](../../guides/live-queries).

## Basic Usage

### injectLiveQuery

The `injectLiveQuery` function creates a live query that automatically updates your component when data changes. It returns an object containing Angular signals for reactive state management:

```typescript
import { Component } from '@angular/core'
import { injectLiveQuery } from '@tanstack/angular-db'
import { eq } from '@tanstack/db'

@Component({
selector: 'app-todo-list',
standalone: true,
template: `
@if (query.isLoading()) {
<div>Loading...</div>
} @else {
<ul>
@for (todo of query.data(); track todo.id) {
<li>{{ todo.text }}</li>
}
</ul>
}
`
})
export class TodoListComponent {
query = injectLiveQuery((q) =>
q.from({ todos: todosCollection })
.where(({ todos }) => eq(todos.completed, false))
.select(({ todos }) => ({ id: todos.id, text: todos.text }))
)
}
```

**Note:** All return values (`data`, `isLoading`, `status`, etc.) are Angular signals, so call them with `()` in your template: `query.data()`, `query.isLoading()`.

> **Template Syntax:** Examples use Angular 17+ control flow (`@if`, `@for`). For Angular 16, use `*ngIf` and `*ngFor` instead.

### Reactive Parameters

For queries that depend on reactive values, use the `params` option to re-run the query when those values change:

```typescript
import { Component, signal } from '@angular/core'
import { injectLiveQuery } from '@tanstack/angular-db'
import { gt } from '@tanstack/db'

@Component({
selector: 'app-filtered-todos',
standalone: true,
template: `
<div>{{ query.data().length }} high-priority todos</div>
`
})
export class FilteredTodosComponent {
minPriority = signal(5)

query = injectLiveQuery({
params: () => ({ minPriority: this.minPriority() }),
query: ({ params, q }) =>
q.from({ todos: todosCollection })
.where(({ todos }) => gt(todos.priority, params.minPriority))
})
}
```

#### When to Use Reactive Parameters

Use the reactive `params` option when your query depends on:
- Component signals
- Input properties
- Computed values
- Other reactive state

When any reactive value accessed in the `params` function changes, the query is recreated and re-executed.

#### What Happens When Parameters Change

When a parameter value changes:
1. The previous live-query collection is disposed
2. A new query is created with the updated parameter values
3. `status()`/`isLoading()` reflect the new query's lifecycle
4. `data()` updates automatically when the new results arrive

#### Best Practices

**Use reactive params for dynamic queries:**

```typescript
import { Component, Input, signal } from '@angular/core'
import { injectLiveQuery } from '@tanstack/angular-db'
import { eq, and } from '@tanstack/db'

@Component({
selector: 'app-todo-list',
standalone: true,
template: `<div>{{ query.data().length }} todos</div>`
})
export class TodoListComponent {
// Angular 16+ compatible input
@Input({ required: true }) userId!: number
status = signal('active')

// Good - reactive params track all dependencies
query = injectLiveQuery({
params: () => ({
userId: this.userId,
status: this.status()
}),
query: ({ params, q }) =>
q.from({ todos: todosCollection })
.where(({ todos }) => and(
eq(todos.userId, params.userId),
eq(todos.status, params.status)
))
})
}
```

**Using Angular 17+ signal inputs:**

```typescript
import { Component, input, signal } from '@angular/core'
import { injectLiveQuery } from '@tanstack/angular-db'
import { eq, and } from '@tanstack/db'

@Component({
selector: 'app-todo-list',
standalone: true,
template: `<div>{{ query.data().length }} todos</div>`
})
export class TodoListComponent {
// Angular 17+ signal-based input
userId = input.required<number>()
status = signal('active')

query = injectLiveQuery({
params: () => ({
userId: this.userId(),
status: this.status()
}),
query: ({ params, q }) =>
q.from({ todos: todosCollection })
.where(({ todos }) => and(
eq(todos.userId, params.userId),
eq(todos.status, params.status)
))
})
}
```

**Static queries don't need params:**

```typescript
import { Component } from '@angular/core'
import { injectLiveQuery } from '@tanstack/angular-db'

@Component({
selector: 'app-all-todos',
standalone: true,
template: `<div>{{ query.data().length }} todos</div>`
})
export class AllTodosComponent {
// No reactive dependencies - query never changes
query = injectLiveQuery((q) =>
q.from({ todos: todosCollection })
)
}
```

**Access multiple signals in template:**

```typescript
import { Component } from '@angular/core'
import { injectLiveQuery } from '@tanstack/angular-db'
import { eq } from '@tanstack/db'

@Component({
selector: 'app-todos',
standalone: true,
template: `
<div>Status: {{ query.status() }}</div>
<div>Loading: {{ query.isLoading() }}</div>
<div>Ready: {{ query.isReady() }}</div>
<div>Total: {{ query.data().length }}</div>
`
})
export class TodosComponent {
query = injectLiveQuery((q) =>
q.from({ todos: todosCollection })
.where(({ todos }) => eq(todos.completed, false))
)
}
```
188 changes: 186 additions & 2 deletions docs/framework/solid/overview.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,189 @@
---
title: TanStack DB Solid Adapter
ref: docs/framework/react/adapter.md
replace: { "React": "Solid", "react": "solid" }
id: adapter
---

## Installation

```sh
npm install @tanstack/solid-db
```

## Solid Primitives

See the [Solid Functions Reference](../reference/index.md) to see the full list of primitives available in the Solid Adapter.

For comprehensive documentation on writing queries (filtering, joins, aggregations, etc.), see the [Live Queries Guide](../../guides/live-queries).

## Basic Usage

### useLiveQuery

The `useLiveQuery` primitive creates a live query that automatically updates your component when data changes. It returns an object where `data` is a plain array and status fields (e.g. `isLoading()`, `status()`) are accessors:

```tsx
import { useLiveQuery } from '@tanstack/solid-db'
import { eq } from '@tanstack/db'
import { Show, For } from 'solid-js'

function TodoList() {
const query = useLiveQuery((q) =>
q.from({ todos: todosCollection })
.where(({ todos }) => eq(todos.completed, false))
.select(({ todos }) => ({ id: todos.id, text: todos.text }))
)

return (
<Show when={!query.isLoading()} fallback={<div>Loading...</div>}>
<ul>
<For each={query.data}>
{(todo) => <li>{todo.text}</li>}
</For>
</ul>
</Show>
)
}
```

**Note:** `query.data` returns an array directly (not a function), but status fields like `isLoading()`, `status()`, etc. are accessor functions.

### Reactive Queries with Signals

Solid uses fine-grained reactivity, which means queries automatically track and respond to signal changes. Simply call signals inside your query function, and Solid will automatically recompute when they change:

```tsx
import { createSignal } from 'solid-js'
import { useLiveQuery } from '@tanstack/solid-db'
import { gt } from '@tanstack/db'

function FilteredTodos(props: { minPriority: number }) {
const query = useLiveQuery((q) =>
q.from({ todos: todosCollection })
.where(({ todos }) => gt(todos.priority, props.minPriority))
)

return <div>{query.data.length} high-priority todos</div>
}
```

When `props.minPriority` changes, Solid's reactivity system automatically:
1. Detects the prop access inside the query function
2. Cleans up the previous live query collection
3. Creates a new query with the updated value
4. Updates the component with the new data

#### Using Signals from Component State

```tsx
import { createSignal } from 'solid-js'
import { useLiveQuery } from '@tanstack/solid-db'
import { eq, and } from '@tanstack/db'

function TodoList() {
const [userId, setUserId] = createSignal(1)
const [status, setStatus] = createSignal('active')

// Solid automatically tracks userId() and status() calls
const query = useLiveQuery((q) =>
q.from({ todos: todosCollection })
.where(({ todos }) => and(
eq(todos.userId, userId()),
eq(todos.status, status())
))
)

return (
<div>
<select onChange={(e) => setStatus(e.currentTarget.value)}>
<option value="active">Active</option>
<option value="completed">Completed</option>
</select>
<div>{query.data.length} todos</div>
</div>
)
}
```

**Key Point:** Unlike React, you don't need dependency arrays. Solid's reactive system automatically tracks any signals, props, or stores accessed during query execution.

#### Best Practices

**Access signals inside the query function:**

```tsx
import { createSignal } from 'solid-js'
import { useLiveQuery } from '@tanstack/solid-db'
import { gt } from '@tanstack/db'

function TodoList() {
const [minPriority, setMinPriority] = createSignal(5)

// Good - signal accessed inside query function
const query = useLiveQuery((q) =>
q.from({ todos: todosCollection })
.where(({ todos }) => gt(todos.priority, minPriority()))
)

// Solid automatically tracks minPriority() and recomputes when it changes
return <div>{query.data.length} todos</div>
}
```

**Don't read signals outside the query function:**

```tsx
import { createSignal } from 'solid-js'
import { useLiveQuery } from '@tanstack/solid-db'
import { gt } from '@tanstack/db'

function TodoList() {
const [minPriority, setMinPriority] = createSignal(5)

// Bad - reading signal outside query function
const currentPriority = minPriority()
const query = useLiveQuery((q) =>
q.from({ todos: todosCollection })
.where(({ todos }) => gt(todos.priority, currentPriority))
)
// Won't update when minPriority changes!

return <div>{query.data.length} todos</div>
}
```

**Static queries need no special handling:**

```tsx
import { useLiveQuery } from '@tanstack/solid-db'

function AllTodos() {
// No signals accessed - query never changes
const query = useLiveQuery((q) =>
q.from({ todos: todosCollection })
)

return <div>{query.data.length} todos</div>
}
```

### Using Pre-created Collections

You can also pass an existing collection to `useLiveQuery`. This is useful for sharing queries across components:

```tsx
import { createLiveQueryCollection } from '@tanstack/db'
import { useLiveQuery } from '@tanstack/solid-db'

// Create collection outside component
const todosQuery = createLiveQueryCollection((q) =>
q.from({ todos: todosCollection })
.where(({ todos }) => eq(todos.active, true))
)

function TodoList() {
// Pass existing collection
const query = useLiveQuery(() => todosQuery)

return <div>{query.data.length} todos</div>
}
```
Loading
Loading