import { Component } from '@angular/core';
import { WrButton } from 'ngwr/button';
import { WrInput } from 'ngwr/input';
import { WrPagination } from 'ngwr/pagination';
import { wrQueryParam } from 'ngwr/utils/router';
@Component({ imports: [WrButton, WrInput, WrPagination] })
export class MyComponent {}wrQueryParam
A writable signal linked to one query parameter: follows the address, writes it on set, one navigation per task, the default left out.
Installation
Import the class, and @use the entry point in your global styles. Nothing warns you when the @use is missing.
Usage
set and update work as on any signal. The signal takes the value at once, and one navigation per task carries every write made in it. Needs an injection context. Lives in ngwr/utils/router, since @angular/router is an optional peer.
import { wrQueryParam } from 'ngwr/utils/router';
@Component({
template: `
<input wrInput type="search" [value]="q()" (input)="q.set($any($event.target).value)" />
<wr-pagination [total]="total()" [(page)]="page" />
`,
})
export class Orders {
readonly q = wrQueryParam('q', { replaceUrl: true });
readonly page = wrQueryParam<number>('page', { default: 1, parse: raw => Number(raw) || 1, serialize: String });
readonly orders = httpResource<Order[]>(() => `/api/orders?q=${this.q()}&page=${this.page()}`);
}A typed parameter
A string needs no options. Any other type needs default, parse and serialize, and the compiler refuses one without them. The address is user input: a parse that throws reads as default.
readonly tags = wrQueryParam<string[]>('tags', {
default: [],
parse: raw => raw.split(','),
serialize: tags => tags.join(','),
});
readonly sort = wrQueryParam<'asc' | 'desc'>('sort', {
default: 'asc',
parse: raw => (raw === 'desc' ? 'desc' : 'asc'),
serialize: String,
});
// Refused by the compiler: a number needs parse and serialize.
readonly page = wrQueryParam<number>('page', { default: 1 });Rules
| When | What happens |
|---|---|
| The parameter is absent | Reads as default. A repeated one reads its first value. |
A write of default | Removes the parameter. Already absent: no navigation. |
| Several writes in one task | One navigation, across every signal. Other parameters and the fragment survive. |
replaceUrl differs within one task | The navigation pushes. A replaced entry is lost; a pushed one is one more Back. |
| A navigation is in flight | The write waits, then lands on the address it produced. |
| Its navigation is cancelled or fails | The signal reads the address again. A lost write is never kept. |
| The injection context is destroyed | Following stops. A write already made is navigated, unless it was waiting for the navigation that destroyed it. |
| On the server | Reads from the router. A write moves the signal only; prerender and hydration navigate nothing. |
API
| Name | Description | Type | Default |
|---|---|---|---|
wrQueryParam(name, options?) | A WritableSignal<string> linked to the name parameter. options.default is what an absent parameter reads as and the value that removes it; options.replaceUrl replaces the history entry instead of pushing one. Needs an injection context. | (name: string, options?: WrQueryParamStringOptions) => WritableSignal<string> | - |
wrQueryParam<T>(name, options) | The typed form: options.default, options.parse and options.serialize are required. A parse that throws reads as default. serialize is called once for default, so a value that serializes like it is left out of the address too. | <T>(name: string, options: WrQueryParamOptions<T>) => WritableSignal<T> | - |
WrQueryParamStringOptions | default (an empty string unless given) and replaceUrl. | { default?: string; replaceUrl?: boolean } | - |
WrQueryParamOptions<T> | default, parse, serialize and replaceUrl. | { default: T; parse: (raw: string) => T; serialize: (value: T) => string; replaceUrl?: boolean } | - |