Skip to main content

angular-db

Angular bindings for TanStack DB. injectLiveQuery inject function with Angular signals (Signal<T>) for all return values. Reactive params pattern ({ params: () => T, query: ({ params, q }) => QueryBuilder }) for dynamic queries. Must be called in injection context. Angular 17+ control flow (@if, @for) and signal inputs supported. Import from @tanstack/angular-db (re-exports all of @tanstack/db).

소스 정보

저장소
TanStack/db
최근 소스 활동
2026년 10월 15일 18:49
감지된 SKILL.md 언어
영어
스타
3,945
포크
276

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
angular-db
description
Angular bindings for TanStack DB. injectLiveQuery inject function with Angular signals (Signal<T>) for all return values. Reactive params pattern ({ params: () => T, query: ({ params, q }) => QueryBuilder }) for dynamic queries. Must be called in injection context. Angular 17+ control flow (@if, @for) and signal inputs supported. Import from @tanstack/angular-db (re-exports all of @tanstack/db).
type
framework
library
db
framework
angular
library_version
0.6.17
requires
["db-core"]
sources
["TanStack/db:docs/framework/angular/overview.md","TanStack/db:packages/angular-db/src/index.ts"]
This skill builds on db-core. Read it first for collection setup, query builder, and mutation patterns. # TanStack DB — Angular ## Setup ```typescript import { Component } from '@angular/core' import { injectLiveQuery, eq, not } from '@tanstack/angular-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 }) => not(todos.completed)) .orderBy(({ todos }) => todos.created_at, 'asc'), ) } ``` `@tanstack/angular-db` re-exports everything from `@tanstack/db`. ## Inject Function ### injectLiveQuery Returns an object with Angular `Signal<T>` properties — call with `()` in templates: ```typescript // Static query — no reactive dependencies const query = injectLiveQuery((q) => q.from({ todo: todoCollection })) // query.data() → Array<T> // query.status() → CollectionStatus | 'disabled' // query.isLoading(), query.isReady(), query.isError() // query.isIdle(), query.isCleanedUp() (seldom used) // query.state() → Map<TKey, T> // query.collection() → Collection | null // Reactive params — re-runs when params change const query = injectLiveQuery({ params: () => ({ minPriority: this.minPriority() }), query: ({ params, q }) => q .from({ todo: todoCollection }) .where(({ todo }) => gt(todo.priority, params.minPriority)), }) // Config object const query = injectLiveQuery({ query: (q) => q.from({ todo: todoCollection }), gcTime: 60000, }) // Pre-created collection const query = injectLiveQuery(preloadedCollection) // Conditional query — return undefined/null to disable const query = injectLiveQuery({ params: () => ({ userId: this.userId() }), query: ({ params, q }) => { if (!params.userId) return undefined return q .from({ todo: todoCollection }) .where(({ todo }) => eq(todo.userId, params.userId)) }, }) ``` A bare `{ query }` config defaults to `startSync: true` and `gcTime: 1`, like the query-function overload. Explicit values in the config override those defaults. ## Angular-Specific Patterns ### Reactive params with signals ```typescript @Component({ selector: 'app-filtered-todos', standalone: true, template: `<div>{{ query.data().length }} 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 `params()` return value changes, the previous collection is disposed and a new query is created. ### Signal inputs (Angular 17+) ```typescript @Component({ selector: 'app-user-todos', standalone: true, template: `<div>{{ query.data().length }} todos</div>`, }) export class UserTodosComponent { userId = input.required<number>() query = injectLiveQuery({ params: () => ({ userId: this.userId() }), query: ({ params, q }) => q .from({ todo: todoCollection }) .where(({ todo }) => eq(todo.userId, params.userId)), }) } ``` ### Legacy @Input (Angular 16) ```typescript export class UserTodosComponent { @Input({ required: true }) userId!: number query = injectLiveQuery({ params: () => ({ userId: this.userId }), query: ({ params, q }) => q .from({ todo: todoCollection }) .where(({ todo }) => eq(todo.userId, params.userId)), }) } ``` ### Template syntax Angular 17+ control flow: ```html @if (query.isLoading()) { <div>Loading...</div> } @else { @for (todo of query.data(); track todo.id) { <li>{{ todo.text }}</li> } } ``` Angular 16 structural directives: ```html <div *ngIf="query.isLoading()">Loading...</div> <li *ngFor="let todo of query.data(); trackBy: trackById">{{ todo.text }}</li> ``` ## Includes (Hierarchical Data) When a query uses includes (subqueries in `select`), each child field is a live `Collection` by default. Subscribe to it with `injectLiveQuery` in a child component: ```typescript @Component({ selector: 'app-project-list', standalone: true, imports: [IssueListComponent], template: ` @for (project of query.data(); track project.id) { <div> {{ project.name }} <app-issue-list [issuesCollection]="project.issues" /> </div> } `, }) export class ProjectListComponent { query = injectLiveQuery((q) => q.from({ p: projectsCollection }).select(({ p }) => ({ id: p.id, name: p.name, issues: q .from({ i: issuesCollection }) .where(({ i }) => eq(i.projectId, p.id)) .select(({ i }) => ({ id: i.id, title: i.title })), })), ) } // Child component subscribes to the child Collection @Component({ selector: 'app-issue-list', standalone: true, template: ` @for (issue of query.data(); track issue.id) { <li>{{ issue.title }}</li> } `, }) export class IssueListComponent { issuesCollection = input.required<Collection>() query = injectLiveQuery(this.issuesCollection()) } ``` With `toArray()`, child results are plain arrays and the parent re-emits on child changes: ```typescript import { toArray, eq } from '@tanstack/angular-db' query = injectLiveQuery((q) => q.from({ p: projectsCollection }).select(({ p }) => ({ id: p.id, name: p.name, issues: toArray( q .from({ i: issuesCollection }) .where(({ i }) => eq(i.projectId, p.id)) .select(({ i }) => ({ id: i.id, title: i.title })), ), })), ) // project.issues is a plain array — no child component subscription needed ``` See db-core/live-queries/SKILL.md for full includes rules (correlation conditions, nested includes, aggregates). ## Common Mistakes ### CRITICAL Using injectLiveQuery outside injection context Wrong: ```typescript export class TodoComponent { ngOnInit() { this.query = injectLiveQuery((q) => q.from({ todo: todoCollection })) } } ``` Correct: ```typescript export class TodoComponent { query = injectLiveQuery((q) => q.from({ todo: todoCollection })) } ``` `injectLiveQuery` calls `assertInInjectionContext` internally — it must be called during construction (field initializer or constructor), not in lifecycle hooks. Source: packages/angular-db/src/index.ts ### HIGH Using query function for reactive values instead of params Wrong: ```typescript export class FilteredComponent { status = signal('active') query = injectLiveQuery((q) => q .from({ todo: todoCollection }) .where(({ todo }) => eq(todo.status, this.status())), ) } ``` Correct: ```typescript export class FilteredComponent { status = signal('active') query = injectLiveQuery({ params: () => ({ status: this.status() }), query: ({ params, q }) => q .from({ todo: todoCollection }) .where(({ todo }) => eq(todo.status, params.status)), }) } ``` The plain query function overload does not track Angular signal reads. Use the `params` pattern to make reactive values trigger query re-creation. Source: packages/angular-db/src/index.ts ### MEDIUM Forgetting to call signals in templates Wrong: ```html <div>{{ query.data.length }}</div> ``` Correct: ```html <div>{{ query.data().length }}</div> ``` All return values are Angular signals. Without `()`, you get the signal object, not the value. See also: db-core/live-queries/SKILL.md — for query builder API. See also: db-core/mutations-optimistic/SKILL.md — for mutation patterns.
GitHub에서 보기