サンプルコードで身につける: クエリパラメータ — 検索条件をURLに載せる
解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。
1queryParamMap でクエリを読み取る
クエリパラメータ読み取りの最小例です。ルートパラメータの paramMap と対になる queryParamMap から、? 以降の値を名前で取り出します。/items?keyword=りんご のようなURLで keyword の値が手に入ります。無ければ null になる点に注意してください。
import { Component, inject } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-item-search',
template: '<p>検索ワード: {{ keyword }}</p>',
})
export class ItemSearch {
private route = inject(ActivatedRoute);
// /items?keyword=りんご なら 'りんご' が入る(無ければ null)
keyword = this.route.snapshot.queryParamMap.get('keyword');
}
2[queryParams] 付きのリンクを作る
リンク側からクエリを付ける定番の書き方です。routerLink にパスを、[queryParams] にオブジェクトを渡すと、?sort=price&order=asc のようなURLが組み立てられます。並び替えボタンをリンクとして作れるため、右クリックで新しいタブに開いても同じ並び順が再現されます。
import { Component } from '@angular/core';
import { RouterLink } from '@angular/router';
@Component({
selector: 'app-sort-menu',
imports: [RouterLink],
// /items?sort=price や /items?sort=new へのリンクになる
template:
'<nav>' +
' <a [routerLink]="[\'/items\']" [queryParams]="{ sort: \'new\' }">新着順</a>' +
' <a [routerLink]="[\'/items\']" [queryParams]="{ sort: \'price\' }">価格順</a>' +
'</nav>',
})
export class SortMenu {}
3ページ番号を数値に変換して初期値も持つ
クエリの値は常に文字列で、無いときは null です。Number() での変換と ?? での初期値をセットにしておくと、?page= が無い初回アクセスでも1ページ目として扱えます。一覧ページのページネーションでそのまま使える形です。
import { Component, inject } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-item-list',
template: '<p>{{ page }}ページ目(1ページ{{ perPage }}件)</p>',
})
export class ItemList {
private route = inject(ActivatedRoute);
// 文字列を数値に変換し、無ければ初期値を使う
page = Number(this.route.snapshot.queryParamMap.get('page') ?? '1');
perPage = Number(
this.route.snapshot.queryParamMap.get('perPage') ?? '20'
);
}
4navigate でクエリを付けて遷移する
検索フォームの送信後など、コード側からクエリ付きで遷移する例です。navigate の第2引数の queryParams にオブジェクトを渡します。「入力された条件をURLに載せて一覧へ飛ぶ」という流れにすると、検索結果ページ自体が共有可能なURLになります。
import { Component, inject, signal } from '@angular/core';
import { Router } from '@angular/router';
@Component({
selector: 'app-search-form',
template:
'<input #box (input)="keyword.set(box.value)" />' +
'<button (click)="search()">検索</button>',
})
export class SearchForm {
private router = inject(Router);
keyword = signal('');
search() {
// /items?keyword=入力値 へ遷移する
this.router.navigate(['/items'], {
queryParams: { keyword: this.keyword() },
});
}
}
5クエリの変化を購読して絞り込みに反映する
同じ一覧ページ内で ?sort= だけが変わる遷移では、コンポーネントは作り直されないため snapshot だと古い値のままです。queryParamMap を subscribe すると、クエリが変わるたびにコールバックが呼ばれ、表示を追従させられます。snapshot との使い分けがクエリ最大のつまずきポイントです。
import { Component, inject, signal } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
const ITEMS = [
{ name: 'りんご', price: 150 },
{ name: 'メロン', price: 800 },
];
@Component({
selector: 'app-item-list',
template:
'@for (item of items(); track item.name) {' +
' <p>{{ item.name }} {{ item.price }}円</p> }',
})
export class ItemList {
private route = inject(ActivatedRoute);
items = signal(ITEMS);
ngOnInit() {
// 同一ページ内でクエリが変わっても呼ばれる
this.route.queryParamMap.subscribe((params) => {
const sort = params.get('sort') ?? 'name';
const sorted = [...ITEMS].sort((a, b) =>
sort === 'price' ? a.price - b.price : a.name.localeCompare(b.name)
);
this.items.set(sorted);
});
}
}