| title | TanStack DB Angular Adapter |
|---|---|
| id | adapter |
npm install @tanstack/angular-dbSee 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.
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*ngIfand*ngForinstead.
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))
})
}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.
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 lifecycledata()updates automatically when the new results arrive
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))
)
}