QueryParams
Reactive wrapper around Angular Router's query parameters. Access the query parameters as a writable signal that can be set to update the URL. Optionally validate parameters at runtime using schema validators for type checking, type coercion, and error handling.
Usage
Basic usage
import { Component } from '@angular/core';
import { queryParams } from '@signality/core';
@Component({
template: `
<p>Search: {{ queryParams().q }}</p>
<p>Sort: {{ queryParams().sort }}</p>
<button (click)="sortByDate()">Sort by date</button>
`,
})
export class SearchPage {
readonly queryParams = queryParams<{ q?: string; sort?: string }>();
sortByDate() {
this.queryParams.update(val => ({ ...val, sort: 'date' }));
}
}With schema validation
import { Component } from '@angular/core';
import { queryParams } from '@signality/core';
import { z } from 'zod';
const schema = z.object({
q: z.string().optional(),
page: z.coerce.number().default(1),
});
@Component({
template: `
<p>Search: {{ params.value().q }}</p>
<p>Page: {{ params.value().page }}</p>
<button (click)="nextPage()">Next page</button>
`,
})
export class SearchPage {
readonly params = queryParams({ schema });
nextPage() {
this.params.value.update(val => ({ ...val, page: val.page + 1 }));
}
}Parameters
| Parameter | Type | Description |
|---|---|---|
options | QueryParamsOptions<T> | QueryParamsWithSchemaOptions<T> | Optional configuration (see Options below). When schema is provided, enables validation and returns QueryParamsRef<T>. |
Options
The options object extends CreateSignalOptions<T> and WithInjector:
| Option | Type | Default | Description |
|---|---|---|---|
equal | ValueEqualityFn<T> | - | Custom equality function (see more) |
debugName | string | - | Debug name for the signal (development only) |
schema | QueryParamsValidator<T> | - | Optional. Validator schema for runtime validation. When provided, returns QueryParamsRef<T> instead of WritableSignal<T>. See Schema validation for details. |
injector | Injector | - | Optional injector for DI context |
replaceUrl | boolean | false | When true, updating the query parameters will replace the current state in history. (see more) |
Return Value
The return type depends on whether a schema is provided:
Without schema
Returns WritableSignal<T> containing the current query parameters, where T is an object with string keys and values of any type (defaults to Record<string, any>).
With schema
Returns QueryParamsRef<T> — an object with the following properties:
| Property | Type | Description |
|---|---|---|
value | WritableSignal<T> | Writable signal containing validated and transformed query parameters. Reading this signal throws an error if validation failed. Use isValid() to check before reading. |
isValid | Signal<boolean> | Signal indicating whether the current query parameters are valid according to the schema. true when valid, false when validation fails. |
error | Signal<unknown | null> | Signal containing the validation error object, or null if the parameters are valid. |
Updating query params
Writing to the signal navigates to the current route with the given query parameters:
- The whole set of query parameters is replaced, so the signal value always matches the URL. Use
update()to merge into the existing ones, andset({})to drop them all. - Parameters whose value is
nullorundefinedare omitted from the URL. - The URL fragment is preserved.
- Writes are asynchronous: the signal is only updated once the navigation succeeds. When the navigation is cancelled (e.g. by a guard), the signal keeps the current parameters.
import { Component } from '@angular/core';
import { queryParams } from '@signality/core';
@Component({ /* ... */ })
export class ProductsPage {
// Route: /products?category=shoes&sort=price&page=1
readonly params = queryParams<{ category?: string; sort?: string; page?: number }>();
filterBy(category: string) {
this.params.update(params => ({ ...params, category, page: 1 })); // ?category=hats&sort=price&page=1
}
clearSort() {
this.params.update(({ sort, ...params }) => params); // ?category=shoes&page=1
}
reset() {
this.params.set({}); // no query params left
}
}Examples
Accessing individual query params
import { Component, computed } from '@angular/core';
import { queryParams } from '@signality/core';
@Component({ /* ... */ })
export class SearchResults {
readonly queryParams = queryParams<{ q?: string; page?: string }>();
readonly search = computed(() => this.queryParams().q ?? '');
readonly page = computed(() => Number(this.queryParams().page ?? '1'));
}Schema validation
Validate query parameters at runtime using schema validators like Zod. When a schema is provided, queryParams returns a QueryParamsRef object with validation status and error information.
Reading value() in error state
Reading the value() signal on a QueryParamsRef that is in an error state throws at runtime. It is recommended to guard value() reads with isValid().
if (params.isValid()) {
// Safe to read params.value()
const data = params.value();
}import { Component, effect, signal } from '@angular/core';
import { queryParams } from '@signality/core';
import { z } from 'zod';
const searchSchema = z.object({
q: z.string().min(1).optional(),
page: z.coerce.number().int().positive().default(1),
});
@Component({ /* ... */ })
export class SearchPage {
readonly params = queryParams({ schema: searchSchema });
constructor() {
effect(() => {
if (this.params.isValid()) {
const { q, page } = this.params.value();
this.searchProducts(q, page);
}
});
}
async searchProducts(q?: string, page = 1) {
// API call implementation
}
}Custom validators
You can use any validator that implements the QueryParamsValidator interface:
interface QueryParamsValidator<T> {
parse(data: unknown): T;
}const pageSchema = {
parse(data: unknown): { page: string } {
const params = data as { page?: string };
const page = Number(params.page);
if (isNaN(page) || page < 1) {
throw new Error('Page must be a positive number');
}
return { page };
},
};SSR Compatibility
On the server, the signal initializes with the query params from the snapshot.
Type Definitions
interface QueryParamsValidator<T> {
parse(data: unknown): T;
}
interface QueryParamsRef<T> {
readonly value: WritableSignal<T>;
readonly isValid: Signal<boolean>;
readonly error: Signal<unknown | null>;
}
type QueryParamsOptions<T extends Record<string, any> = Record<string, any>> = CreateSignalOptions<T> &
WithInjector &
Pick<NavigationExtras, 'replaceUrl'>;
type QueryParamsWithSchemaOptions<T extends Record<string, any> = Record<string, any>> = QueryParamsOptions<T> & {
readonly schema: QueryParamsValidator<T>;
};
function queryParams<T extends Record<string, any> = Record<string, any>>(
options?: QueryParamsOptions<T>
): WritableSignal<T>;
function queryParams<T extends Record<string, any> = Record<string, any>>(
options: QueryParamsWithSchemaOptions<T>
): QueryParamsRef<T>;Related
- params — Access route parameters
- fragment — Access URL fragment
- url — Access current URL
- proxySignal — Intercept signal reads and writes