TanStack DB Angular Adapter
Installation#
npm install @tanstack/angular-dbAngular inject function#
See the Angular Functions Reference 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.
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:
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:
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:
- The previous live-query collection is disposed
- A new query is created with the updated parameter values
- status()/isLoading() reflect the new query's lifecycle
- data() updates automatically when the new results arrive
Best Practices#
Use reactive params for dynamic queries:
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:
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:
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:
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))
)
}