@dasch-ng/route-signals

中文文档
1.1.0发布于 2026/11/07TypeScriptES Module构建来源已验证近 30 天下载 2135

npm version

原始使用文档
文档目录18 节
  1. Features
  2. Installation
  3. Usage
  4. Route Parameters
  5. Query Parameters
  6. Route Data
  7. With Resolvers
  8. API
  9. routeParam(key: string): Signal<string>
  10. routeQueryParam(key: string): Signal<string>
  11. routeData<T>(key: string): Signal<T>
  12. bindQuerySignal<T>(options: BindQuerySignalOptions<T>): void
  13. Query parameter codecs
  14. URL Decoding
  15. Error Handling
  16. Requirements
  17. License
  18. Links

@dasch-ng/route-signals

npm version

Angular utilities for working with route parameters, query parameters, and route data as signals, plus writing signals back into query parameters.

Features

  • 🎯 Type-safe signals - Get route state with full TypeScript support
  • 🔄 Automatic updates - Signals update when route changes
  • 🔐 URL decoding - Automatically decodes URL-encoded values
  • 🛡️ Error handling - Safe decoding with fallback for malformed URLs
  • ⚡ Zero dependencies - Only depends on Angular and RxJS
  • 📦 Tree-shakeable - Import only what you need

Installation

npm install @dasch-ng/route-signals

Usage

Route Parameters

Track URL path parameters as signals:

import { Component } from '@angular/core';
import { routeParam } from '@dasch-ng/route-signals';

@Component({
  selector: 'app-user-detail',
  template: `
    <h1>User: {{ userId() }}</h1>
    <p>Group: {{ groupId() }}</p>
  `,
})
export class UserDetailComponent {
  // Route: /users/:userId/groups/:groupId
  readonly userId = routeParam('userId');
  readonly groupId = routeParam('groupId');

  constructor() {
    // URL: /users/john-doe/groups/admin
    console.log(this.userId()); // "john-doe"
    console.log(this.groupId()); // "admin"

    // Automatically decodes URL-encoded values
    // URL: /users/john%20doe/groups/team%20alpha
    console.log(this.userId()); // "john doe"
    console.log(this.groupId()); // "team alpha"
  }
}

Query Parameters

Track URL query parameters as signals:

import { Component } from '@angular/core';
import { routeQueryParam } from '@dasch-ng/route-signals';

@Component({
  selector: 'app-search',
  template: `
    <h1>Search: {{ query() }}</h1>
    <p>Filter: {{ filter() }}</p>
    <p>Sort: {{ sort() }}</p>
  `,
})
export class SearchComponent {
  // URL: /search?q=angular&filter=tutorials&sort=recent
  readonly query = routeQueryParam('q');
  readonly filter = routeQueryParam('filter');
  readonly sort = routeQueryParam('sort');

  constructor() {
    console.log(this.query()); // "angular"
    console.log(this.filter()); // "tutorials"
    console.log(this.sort()); // "recent"

    // Automatically decodes URL-encoded values
    // URL: /search?q=angular%20signals&filter=a%2Fb
    console.log(this.query()); // "angular signals"
    console.log(this.filter()); // "a/b"
  }
}

Route Data

Track route data properties as signals:

import { Component } from '@angular/core';
import { routeData } from '@dasch-ng/route-signals';

// In route configuration:
const routes = [
  {
    path: 'admin',
    component: AdminComponent,
    data: { title: 'Admin Dashboard', role: 'admin' },
  },
];

@Component({
  selector: 'app-admin',
  template: `
    <h1>{{ title() }}</h1>
    <p>Required role: {{ role() }}</p>
  `,
})
export class AdminComponent {
  readonly title = routeData<string>('title');
  readonly role = routeData<string>('role');

  constructor() {
    console.log(this.title()); // "Admin Dashboard"
    console.log(this.role()); // "admin"
  }
}

With Resolvers

Route data works seamlessly with Angular resolvers:

import { inject } from '@angular/core';
import { ActivatedRouteSnapshot } from '@angular/router';
import { UserService } from './user.service';

// Resolver
const userResolver = (route: ActivatedRouteSnapshot) => {
  const userService = inject(UserService);
  const userId = route.paramMap.get('id');
  return userService.getUser(userId);
};

// Route configuration
const routes = [
  {
    path: 'user/:id',
    component: UserDetailComponent,
    resolve: { user: userResolver },
    // Re-run resolver when params change
    runGuardsAndResolvers: 'paramsChange',
  },
];

// Component
@Component({
  selector: 'app-user-detail',
  template: `
    <h1>{{ user().name }}</h1>
    <p>{{ user().email }}</p>
  `,
})
export class UserDetailComponent {
  readonly user = routeData<User>('user');

  // Signal automatically updates when resolver re-runs
}

API

routeParam(key: string): Signal<string>

Creates a signal that tracks a URL-encoded route parameter.

  • Parameters:
    • key: The name of the route parameter to track
  • Returns: A signal containing the decoded parameter value
  • Throws: Error if the parameter is not present in the current route

routeQueryParam(key: string): Signal<string>

Creates a signal that tracks a URL-encoded query parameter.

  • Parameters:
    • key: The name of the query parameter to track
  • Returns: A signal containing the decoded query parameter value
  • Throws: Error if the query parameter is not present in the current route

routeData<T>(key: string): Signal<T>

Creates a signal that tracks a route data property.

  • Type Parameters:
    • T: The type of the data property
  • Parameters:
    • key: The name of the data property to track
  • Returns: A signal containing the route data value
  • Throws: Error if the data property is not present in the current route

bindQuerySignal<T>(options: BindQuerySignalOptions<T>): void

Writes a WritableSignal into a query parameter. The signal is set once from the URL on creation; later changes update the URL (debounced, replaceUrl, other parameters kept). A value that serializes like defaultValue removes the parameter.

  • Options: signal, param, defaultValue, optional serialize(value) => string | null and deserialize(raw) => T
  • The URL is only read on creation. Combine with routeQueryParam to follow back/forward navigation.
export class AssetListComponent {
  readonly search = signal('');
  readonly size = signal<MinMaxRange | null>(null);

  constructor() {
    bindQuerySignal({ signal: this.search, param: 'q', defaultValue: '' });
    bindQuerySignal({
      signal: this.size,
      param: 'size',
      defaultValue: null,
      serialize: serializeMinMaxRange,
      deserialize: deserializeMinMaxRange,
    });
  }
}

Query parameter codecs

Ready-made serialize/deserialize pairs for bindQuerySignal:

  • serializeMinMaxRange / deserializeMinMaxRange: { min: 5, max: 100 } ⇄ 5-100 (open sides: 5-, -100)
  • serializeDateRange / deserializeDateRange: { start, end } ⇄ 2026-01-01,2026-02-15 (local calendar dates)

URL Decoding

All functions automatically decode URL-encoded values using a safe decoder that:

  • Decodes standard URL encoding (e.g., %20 → space)
  • Handles special characters (e.g., %2F → /, %3D → =)
  • Decodes Unicode characters (e.g., %E2%9C%93 → ✓)
  • Safely handles malformed encoding (logs warning and returns original string)

Error Handling

All functions throw an error if the requested parameter/data is not present in the route. This helps catch configuration errors early:

// If route doesn't have an 'id' parameter
const userId = routeParam('id'); // ❌ Throws: "id is not in route."

// If route doesn't have a 'search' query parameter
const search = routeQueryParam('search'); // ❌ Throws: "Query parameter "search" is not in route."

// If route data doesn't have a 'title' property
const title = routeData('title'); // ❌ Throws: "Route data property "title" is not in route."

Requirements

  • Angular 19.0.0 or higher
  • RxJS 7.5.0 or higher

License

MIT © Daniel Schuba

版本与发布记录

@dasch-ng/route-signals 共发布 3 个版本,首次发布于 2025/12/25;过去 12 个月发布了 3 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
1.1.0latest2026/11/076 天前
1.0.02026/02/208 个月前
1.0.0-rc.0rc2025/12/2510 个月前

关于 @dasch-ng/route-signals 的常见问题

如何安装 @dasch-ng/route-signals?

在项目目录运行 npm install @dasch-ng/route-signals,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:rxjs(^7.0.0)、@angular/core(^21.0.0 || ^22.0.0)、@angular/router(^21.0.0 || ^22.0.0)、@dasch-ng/rxjs-operators(^0.3.2 || ^0.4.0 || ^0.5.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@dasch-ng/route-signals 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@dasch-ng/route-signals 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@dasch-ng/route-signals 可以商用吗?

@dasch-ng/route-signals 使用 未声明 许可。package.json 没有声明许可证。没有许可证的代码默认保留全部权利,商用前请联系作者或查看仓库中的 LICENSE 文件。以上为概括,不构成法律意见,请以许可原文为准。

@dasch-ng/route-signals 的下载量有多少?

根据 npm 官方下载统计,@dasch-ng/route-signals 在 2026/10/12 – 2026/11/10 这 30 天内被下载 2,135 次,日均约 71 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@dasch-ng/route-signals 还在维护吗?

最新版本 1.1.0 发布于 2026/11/07(6 天前);至今共发布 3 个版本,其中过去 12 个月发布了 3 个;首次发布于 2025/12/25。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。