@mmstack/router-core 21.0.4 → 21.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -0
- package/fesm2022/mmstack-router-core.mjs +320 -62
- package/fesm2022/mmstack-router-core.mjs.map +1 -1
- package/package.json +1 -1
- package/types/mmstack-router-core.d.ts +303 -58
|
@@ -5,7 +5,19 @@ import { Signal, InjectionToken, Provider, WritableSignal } from '@angular/core'
|
|
|
5
5
|
import { Observable } from 'rxjs';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* A flattened view of one route in the active router chain, used by the
|
|
9
|
+
* breadcrumb and title subsystems. Each `ResolvedLeafRoute` describes one
|
|
10
|
+
* "step" in the chain from root to current leaf.
|
|
11
|
+
*
|
|
12
|
+
* Exposed publicly because custom breadcrumb generators (see
|
|
13
|
+
* {@link BreadcrumbConfig}'s `generation` callback) receive instances of
|
|
14
|
+
* this type and need to read its fields.
|
|
15
|
+
*
|
|
16
|
+
* - `route` — the underlying `ActivatedRouteSnapshot`.
|
|
17
|
+
* - `segment.path` — the route config segment (e.g. `:userId`).
|
|
18
|
+
* - `segment.resolved` — the resolved value of that segment (e.g. `'42'`).
|
|
19
|
+
* - `path` — the full route-config path from root (with raw segments like `:userId`).
|
|
20
|
+
* - `link` — the full resolved URL from root (with substituted values).
|
|
9
21
|
*/
|
|
10
22
|
type ResolvedLeafRoute = {
|
|
11
23
|
route: ActivatedRouteSnapshot;
|
|
@@ -78,31 +90,33 @@ type BreadcrumbConfig = {
|
|
|
78
90
|
};
|
|
79
91
|
/**
|
|
80
92
|
* Provides configuration for the breadcrumb system.
|
|
81
|
-
*
|
|
93
|
+
*
|
|
94
|
+
* @param config A partial {@link BreadcrumbConfig}. The `generation` field controls
|
|
95
|
+
* automatic label generation: `'manual'` disables it (breadcrumbs only show when
|
|
96
|
+
* {@link createBreadcrumb} explicitly registers them); a function provides a
|
|
97
|
+
* custom label generator instead of the default route-title-based one.
|
|
98
|
+
* @returns A `Provider` to add to your app's providers array.
|
|
99
|
+
*
|
|
82
100
|
* @see BreadcrumbConfig
|
|
101
|
+
*
|
|
83
102
|
* @example
|
|
84
|
-
* ```
|
|
85
|
-
* //
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* //
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
* // ...rest
|
|
102
|
-
* provideBreadcrumbConfig({
|
|
103
|
-
* generation: customLabelStrategy, // or 'manual' to disable auto-generation
|
|
104
|
-
* }),
|
|
105
|
-
* ]
|
|
103
|
+
* ```ts
|
|
104
|
+
* // Disable automatic generation — breadcrumbs only appear when createBreadcrumb is used
|
|
105
|
+
* bootstrapApplication(AppComponent, {
|
|
106
|
+
* providers: [
|
|
107
|
+
* provideRouter(routes),
|
|
108
|
+
* provideBreadcrumbConfig({ generation: 'manual' }),
|
|
109
|
+
* ],
|
|
110
|
+
* });
|
|
111
|
+
* ```
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```ts
|
|
115
|
+
* // Custom label strategy — outer fn runs in injection context, inner is reactive
|
|
116
|
+
* const customLabelStrategy = () => (leaf: ResolvedLeafRoute) =>
|
|
117
|
+
* leaf.route.data?.['navTitle'] ?? leaf.route.title ?? 'Unnamed';
|
|
118
|
+
*
|
|
119
|
+
* provideBreadcrumbConfig({ generation: customLabelStrategy });
|
|
106
120
|
* ```
|
|
107
121
|
*/
|
|
108
122
|
declare function provideBreadcrumbConfig(config: Partial<BreadcrumbConfig>): {
|
|
@@ -134,17 +148,24 @@ type CreateBreadcrumbOptions = {
|
|
|
134
148
|
awaitValue?: boolean;
|
|
135
149
|
};
|
|
136
150
|
/**
|
|
137
|
-
* Creates and registers a breadcrumb for a specific route.
|
|
138
|
-
*
|
|
151
|
+
* Creates and registers a breadcrumb for a specific route. Designed to be used
|
|
152
|
+
* as an Angular Route `ResolveFn` in the route's `resolve` map.
|
|
139
153
|
*
|
|
140
|
-
* Accepts a static label
|
|
141
|
-
* use a factory when you need `inject()`
|
|
154
|
+
* Accepts a static label, a static options object, or a factory returning
|
|
155
|
+
* either — use a factory when you need `inject()` to read dynamic data.
|
|
156
|
+
*
|
|
157
|
+
* @param factoryOrValue One of: a literal label string (shorthand for
|
|
158
|
+
* `{ label: <string> }`), a static {@link CreateBreadcrumbOptions} object,
|
|
159
|
+
* or a factory `() => string | CreateBreadcrumbOptions` invoked inside an
|
|
160
|
+
* injection context (so it can use `inject()`).
|
|
161
|
+
* @returns An Angular `ResolveFn<void>` to wire into a route's `resolve` map.
|
|
162
|
+
* The resolver registers the breadcrumb as a side effect; the resolved value
|
|
163
|
+
* itself is unused.
|
|
142
164
|
*
|
|
143
|
-
* @param factoryOrValue A static label, a static `CreateBreadcrumbOptions`, or a factory returning either.
|
|
144
165
|
* @see CreateBreadcrumbOptions
|
|
145
166
|
*
|
|
146
167
|
* @example
|
|
147
|
-
* ```
|
|
168
|
+
* ```ts
|
|
148
169
|
* export const appRoutes: Routes = [
|
|
149
170
|
* {
|
|
150
171
|
* path: 'home',
|
|
@@ -161,7 +182,7 @@ type CreateBreadcrumbOptions = {
|
|
|
161
182
|
* breadcrumb: createBreadcrumb(() => {
|
|
162
183
|
* const userStore = inject(UserStore);
|
|
163
184
|
* return {
|
|
164
|
-
* label: () => userStore.user().name ?? 'Loading
|
|
185
|
+
* label: () => userStore.user().name ?? 'Loading…',
|
|
165
186
|
* };
|
|
166
187
|
* }),
|
|
167
188
|
* },
|
|
@@ -250,7 +271,56 @@ type MMLinkConfig = {
|
|
|
250
271
|
*/
|
|
251
272
|
useMouseDown: boolean;
|
|
252
273
|
};
|
|
274
|
+
/**
|
|
275
|
+
* Provide application-wide defaults for the `mmLink` directive. Each `[mmLink]`
|
|
276
|
+
* instance can still override per-link via its own `preloadOn` / `useMouseDown`
|
|
277
|
+
* inputs; this just shifts the default.
|
|
278
|
+
*
|
|
279
|
+
* @param config Partial override of `MMLinkConfig`. Unset keys fall back to:
|
|
280
|
+
* - `preloadOn: 'hover'` — preload triggered when the user hovers a link
|
|
281
|
+
* - `useMouseDown: false` — navigation triggered on click (not mousedown)
|
|
282
|
+
* @returns A `Provider` to add to your app's providers array.
|
|
283
|
+
*
|
|
284
|
+
* @example
|
|
285
|
+
* ```ts
|
|
286
|
+
* bootstrapApplication(AppComponent, {
|
|
287
|
+
* providers: [
|
|
288
|
+
* provideMMLinkDefaultConfig({ preloadOn: 'visible', useMouseDown: true }),
|
|
289
|
+
* ],
|
|
290
|
+
* });
|
|
291
|
+
* ```
|
|
292
|
+
*/
|
|
253
293
|
declare function provideMMLinkDefaultConfig(config: Partial<MMLinkConfig>): Provider;
|
|
294
|
+
/**
|
|
295
|
+
* Drop-in replacement for `[routerLink]` that adds preloading on hover or
|
|
296
|
+
* visibility, optional mousedown-triggered navigation, and a `beforeNavigate`
|
|
297
|
+
* hook. Composes with Angular's `RouterLink` via `hostDirectives`, so every
|
|
298
|
+
* `RouterLink` input (`target`, `queryParams`, `fragment`, etc.) is forwarded.
|
|
299
|
+
*
|
|
300
|
+
* Preload behavior:
|
|
301
|
+
* - `preloadOn: 'hover'` (default) — preload when the user hovers the link
|
|
302
|
+
* - `preloadOn: 'visible'` — preload when the link scrolls into view
|
|
303
|
+
* - `preloadOn: null` — disable preloading on this link
|
|
304
|
+
*
|
|
305
|
+
* Navigation timing:
|
|
306
|
+
* - `useMouseDown: false` (default) — navigate on click
|
|
307
|
+
* - `useMouseDown: true` — navigate on mousedown (shaves ~50ms but breaks if the user
|
|
308
|
+
* moves off the link before mouseup)
|
|
309
|
+
*
|
|
310
|
+
* Requires {@link PreloadStrategy} to be wired via `provideRouter(routes, withComponentInputBinding(), withPreloading(PreloadStrategy))`.
|
|
311
|
+
* Set app-wide defaults with {@link provideMMLinkDefaultConfig}.
|
|
312
|
+
*
|
|
313
|
+
* @example
|
|
314
|
+
* ```html
|
|
315
|
+
* <a [mmLink]="['/users', userId()]">View profile</a>
|
|
316
|
+
*
|
|
317
|
+
* <!-- Override per-link -->
|
|
318
|
+
* <a [mmLink]="'/heavy-page'" preloadOn="visible" useMouseDown>Heavy page</a>
|
|
319
|
+
*
|
|
320
|
+
* <!-- React to the preload starting -->
|
|
321
|
+
* <a [mmLink]="'/checkout'" (preloading)="onPreload()">Checkout</a>
|
|
322
|
+
* ```
|
|
323
|
+
*/
|
|
254
324
|
declare class Link {
|
|
255
325
|
private readonly routerLink;
|
|
256
326
|
private readonly req;
|
|
@@ -355,47 +425,126 @@ type NavItem<TMeta = Record<string, unknown>> = {
|
|
|
355
425
|
children: Signal<NavItem<TMeta>[]>;
|
|
356
426
|
};
|
|
357
427
|
|
|
428
|
+
/**
|
|
429
|
+
* Per-scope fallback descriptor: a static array or a factory returning one.
|
|
430
|
+
*/
|
|
431
|
+
type NavDefaultsForScope<TMeta = Record<string, unknown>> = CreateNavItem<TMeta>[] | (() => CreateNavItem<TMeta>[]);
|
|
358
432
|
/**
|
|
359
433
|
* Global configuration for the nav system.
|
|
360
434
|
* @see provideNavConfig
|
|
361
435
|
*/
|
|
362
|
-
type NavConfig = {
|
|
436
|
+
type NavConfig<TMeta = Record<string, unknown>> = {
|
|
363
437
|
/**
|
|
364
438
|
* Default match options used when computing `NavItem.active`. Per-item `activeMatch`
|
|
365
439
|
* (and the router's built-in `subsetMatchOptions`) are still merged on top.
|
|
366
440
|
*/
|
|
367
441
|
activeMatch?: Partial<IsActiveMatchOptions>;
|
|
442
|
+
/**
|
|
443
|
+
* Fallback nav items rendered when no route in the active chain has registered items
|
|
444
|
+
* for the requested scope. Relative `link`s resolve from `/`.
|
|
445
|
+
*
|
|
446
|
+
* Forms:
|
|
447
|
+
* - Array (or factory): fallback for the default (unnamed) scope.
|
|
448
|
+
* - Record: keys match the `name` passed to `createNavItems`. The unnamed scope can
|
|
449
|
+
* also be provided via this record using the empty-string key `''`.
|
|
450
|
+
*
|
|
451
|
+
* A route that wants to render an *empty* nav explicitly should register
|
|
452
|
+
* `createNavItems([])`, which shadows these defaults via the normal deepest-wins rule.
|
|
453
|
+
*/
|
|
454
|
+
defaults?: NavDefaultsForScope<TMeta> | Record<string, NavDefaultsForScope<TMeta>>;
|
|
368
455
|
};
|
|
369
456
|
/**
|
|
370
|
-
* Provides global configuration for the nav system.
|
|
457
|
+
* Provides global configuration for the nav system. The factory form runs in
|
|
458
|
+
* an injection context, so it can use `inject()` to build defaults from app
|
|
459
|
+
* state.
|
|
460
|
+
*
|
|
461
|
+
* @param config Either a literal {@link NavConfig} object or a factory
|
|
462
|
+
* `() => NavConfig`. Optional — without it, the nav system uses Angular's
|
|
463
|
+
* default `activeMatch` options and renders nothing in scopes that have no
|
|
464
|
+
* registered items.
|
|
465
|
+
* @returns A `Provider` to add to your app's providers array.
|
|
371
466
|
*
|
|
372
467
|
* @example
|
|
373
|
-
* ```
|
|
374
|
-
*
|
|
375
|
-
*
|
|
376
|
-
*
|
|
468
|
+
* ```ts
|
|
469
|
+
* bootstrapApplication(AppComponent, {
|
|
470
|
+
* providers: [
|
|
471
|
+
* provideRouter(routes),
|
|
472
|
+
* provideNavConfig({
|
|
473
|
+
* activeMatch: { queryParams: 'ignored' },
|
|
474
|
+
* defaults: [
|
|
475
|
+
* { label: 'Home', link: '/' },
|
|
476
|
+
* { label: 'Docs', link: '/docs' },
|
|
477
|
+
* ],
|
|
478
|
+
* }),
|
|
479
|
+
* ],
|
|
480
|
+
* });
|
|
481
|
+
* ```
|
|
482
|
+
*
|
|
483
|
+
* @example
|
|
484
|
+
* ```ts
|
|
485
|
+
* // Factory form — read defaults from a service
|
|
486
|
+
* provideNavConfig(() => {
|
|
487
|
+
* const role = inject(AuthStore).role();
|
|
488
|
+
* return {
|
|
489
|
+
* defaults: {
|
|
490
|
+
* main: role === 'admin' ? adminNav : guestNav,
|
|
491
|
+
* },
|
|
492
|
+
* };
|
|
493
|
+
* });
|
|
377
494
|
* ```
|
|
378
495
|
*/
|
|
379
|
-
declare function provideNavConfig(config?: NavConfig): Provider;
|
|
496
|
+
declare function provideNavConfig(config?: NavConfig | (() => NavConfig)): Provider;
|
|
380
497
|
|
|
381
498
|
/**
|
|
382
499
|
* Registers a set of nav items for the activating route under the given scope.
|
|
383
|
-
* Mirrors
|
|
384
|
-
* `resolve` map.
|
|
500
|
+
* Mirrors {@link createBreadcrumb} / {@link createTitle} — designed to be used
|
|
501
|
+
* in a route's `resolve` map.
|
|
502
|
+
*
|
|
503
|
+
* Scope override semantics: when multiple routes in the active chain register
|
|
504
|
+
* items under the same scope, the deepest active registration wins. Navigating
|
|
505
|
+
* away restores the shallower registration. To explicitly render an empty nav
|
|
506
|
+
* (shadowing a default), pass `[]`.
|
|
507
|
+
*
|
|
508
|
+
* @typeParam TMeta Optional per-item metadata type — flows through the
|
|
509
|
+
* registered items so consumers reading via {@link injectNavItems} get
|
|
510
|
+
* typed access to `item.meta`.
|
|
511
|
+
* @param itemsOrFactory Either a static array of {@link CreateNavItem} or a
|
|
512
|
+
* factory `() => CreateNavItem<TMeta>[]` invoked inside an injection
|
|
513
|
+
* context (so it can use `inject()` for dynamic items).
|
|
514
|
+
* @param options Optional `{ name }` for registering multiple scopes on a
|
|
515
|
+
* single route. Omit to target the default (unnamed) scope.
|
|
516
|
+
* @returns An Angular `ResolveFn<void>` to wire into a route's `resolve` map.
|
|
517
|
+
* The resolver registers items as a side effect; the resolved value itself
|
|
518
|
+
* is unused.
|
|
385
519
|
*
|
|
386
|
-
*
|
|
387
|
-
*
|
|
520
|
+
* @example
|
|
521
|
+
* ```ts
|
|
522
|
+
* // Single default-scope nav
|
|
523
|
+
* {
|
|
524
|
+
* path: 'app',
|
|
525
|
+
* resolve: {
|
|
526
|
+
* _nav: createNavItems([
|
|
527
|
+
* { label: 'Dashboard', link: 'dashboard' },
|
|
528
|
+
* { label: 'Reports', link: 'reports' },
|
|
529
|
+
* ]),
|
|
530
|
+
* },
|
|
531
|
+
* }
|
|
388
532
|
*
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
*
|
|
392
|
-
*
|
|
533
|
+
* // Multiple scopes
|
|
534
|
+
* {
|
|
535
|
+
* path: 'app',
|
|
536
|
+
* resolve: {
|
|
537
|
+
* mainNav: createNavItems([...], { name: 'main' }),
|
|
538
|
+
* sideNav: createNavItems([...], { name: 'side' }),
|
|
539
|
+
* },
|
|
393
540
|
* }
|
|
394
|
-
* ```
|
|
395
541
|
*
|
|
396
|
-
*
|
|
397
|
-
*
|
|
398
|
-
*
|
|
542
|
+
* // Factory using inject()
|
|
543
|
+
* createNavItems(() => {
|
|
544
|
+
* const auth = inject(AuthStore);
|
|
545
|
+
* return auth.canAdmin() ? adminItems : userItems;
|
|
546
|
+
* });
|
|
547
|
+
* ```
|
|
399
548
|
*/
|
|
400
549
|
declare function createNavItems<TMeta = Record<string, unknown>>(itemsOrFactory: CreateNavItem<TMeta>[] | (() => CreateNavItem<TMeta>[]), options?: {
|
|
401
550
|
name?: string;
|
|
@@ -405,7 +554,9 @@ declare function createNavItems<TMeta = Record<string, unknown>>(itemsOrFactory:
|
|
|
405
554
|
* Returns a reactive list of nav items for the requested scope.
|
|
406
555
|
*
|
|
407
556
|
* The returned signal reflects the nearest active ancestor route that registered items
|
|
408
|
-
* for `name` via `createNavItems`.
|
|
557
|
+
* for `name` via `createNavItems`. If no active route has registered items for the
|
|
558
|
+
* scope, falls back to `NavConfig.defaults` (when provided via `provideNavConfig`).
|
|
559
|
+
* Hidden items are filtered out.
|
|
409
560
|
*
|
|
410
561
|
* @typeParam TMeta The shape of `NavItem.meta` for the consuming code. Untyped at the
|
|
411
562
|
* registration site — this is a consumer-side assertion.
|
|
@@ -426,6 +577,44 @@ declare function createNavItems<TMeta = Record<string, unknown>>(itemsOrFactory:
|
|
|
426
577
|
*/
|
|
427
578
|
declare function injectNavItems<TMeta = Record<string, unknown>>(name?: string): Signal<NavItem<TMeta>[]>;
|
|
428
579
|
|
|
580
|
+
/**
|
|
581
|
+
* Demand-driven preloading strategy for Angular's router. Unlike Angular's
|
|
582
|
+
* built-in `PreloadAllModules`, this strategy preloads a lazy route only
|
|
583
|
+
* when something explicitly requests it via {@link PreloadRequester} (e.g.
|
|
584
|
+
* the `mmLink` directive on hover or visibility, or {@link injectTriggerPreload}
|
|
585
|
+
* called imperatively).
|
|
586
|
+
*
|
|
587
|
+
* Skips preloading when:
|
|
588
|
+
* - the route has `data.preload === false`
|
|
589
|
+
* - the network is on `2g` or in `saveData` mode (cheap-data-mode users)
|
|
590
|
+
* - a load for the same path is already in flight
|
|
591
|
+
*
|
|
592
|
+
* Wire this into `provideRouter` to enable the `mmLink` preload pipeline:
|
|
593
|
+
*
|
|
594
|
+
* @example
|
|
595
|
+
* ```ts
|
|
596
|
+
* import { PreloadStrategy } from '@mmstack/router-core';
|
|
597
|
+
* import { provideRouter, withPreloading } from '@angular/router';
|
|
598
|
+
*
|
|
599
|
+
* bootstrapApplication(AppComponent, {
|
|
600
|
+
* providers: [
|
|
601
|
+
* provideRouter(routes, withPreloading(PreloadStrategy)),
|
|
602
|
+
* ],
|
|
603
|
+
* });
|
|
604
|
+
*
|
|
605
|
+
* // Then in templates, `mmLink` (or `injectTriggerPreload`) requests
|
|
606
|
+
* // preloads that this strategy executes:
|
|
607
|
+
* // <a [mmLink]="'/users'">Users</a>
|
|
608
|
+
* ```
|
|
609
|
+
*
|
|
610
|
+
* @example
|
|
611
|
+
* ```ts
|
|
612
|
+
* // Opt a route out of preloading:
|
|
613
|
+
* export const routes: Routes = [
|
|
614
|
+
* { path: 'admin', loadChildren: () => import('./admin'), data: { preload: false } },
|
|
615
|
+
* ];
|
|
616
|
+
* ```
|
|
617
|
+
*/
|
|
429
618
|
declare class PreloadStrategy implements PreloadingStrategy {
|
|
430
619
|
private readonly loading;
|
|
431
620
|
private readonly router;
|
|
@@ -537,19 +726,75 @@ type TitleConfig = {
|
|
|
537
726
|
keepLastKnownTitle?: boolean;
|
|
538
727
|
};
|
|
539
728
|
/**
|
|
540
|
-
*
|
|
729
|
+
* Provide application-wide configuration for the title subsystem. The config
|
|
730
|
+
* is only consumed when at least one route uses a {@link createTitle} resolver;
|
|
731
|
+
* routes without `createTitle` are unaffected.
|
|
732
|
+
*
|
|
733
|
+
* @param config Optional {@link TitleConfig}. All fields are optional — pass
|
|
734
|
+
* `prefix` to namespace titles (e.g. `"My App – "`), `initialTitle` to
|
|
735
|
+
* override the fallback (defaults to the `<title>` from `index.html`), and
|
|
736
|
+
* `keepLastKnownTitle: false` to clear the title on navigations to routes
|
|
737
|
+
* without a title (the default keeps the previous one).
|
|
738
|
+
* @returns A `Provider` to add to your app's providers array.
|
|
739
|
+
*
|
|
740
|
+
* @example
|
|
741
|
+
* ```ts
|
|
742
|
+
* bootstrapApplication(AppComponent, {
|
|
743
|
+
* providers: [
|
|
744
|
+
* provideRouter(routes),
|
|
745
|
+
* provideTitleConfig({
|
|
746
|
+
* prefix: (title) => `${title} • My App`,
|
|
747
|
+
* keepLastKnownTitle: true,
|
|
748
|
+
* }),
|
|
749
|
+
* ],
|
|
750
|
+
* });
|
|
751
|
+
* ```
|
|
541
752
|
*/
|
|
542
753
|
declare function provideTitleConfig(config?: TitleConfig): Provider;
|
|
543
754
|
|
|
544
755
|
/**
|
|
756
|
+
* Creates an Angular router `ResolveFn<string>` that registers a title for the
|
|
757
|
+
* route it's attached to. Titles can be static strings, factory functions
|
|
758
|
+
* (called in an injection context, so they can use `inject()`), or signal
|
|
759
|
+
* factories (for reactive titles that change when underlying data does).
|
|
760
|
+
*
|
|
761
|
+
* The resolved title flows through any `prefix` configured via
|
|
762
|
+
* {@link provideTitleConfig}, and is wired into Angular's `Title` service
|
|
763
|
+
* via an effect. Nested routes pick the most-specific leaf's title; if a
|
|
764
|
+
* deeper route has no title and `keepLastKnownTitle` is `true` (default),
|
|
765
|
+
* the previous title is preserved.
|
|
766
|
+
*
|
|
767
|
+
* @param factoryOrValue Either a literal string title, a `() => string`
|
|
768
|
+
* factory, or a `() => Signal<string>` factory for reactive titles. Factory
|
|
769
|
+
* callbacks run inside an injection context, so they can use `inject()`.
|
|
770
|
+
* @param awaitValue When `true`, the resolver waits until the title signal
|
|
771
|
+
* emits a truthy value before resolving — useful for SSR/SEO where the
|
|
772
|
+
* resolved title should not be empty. Defaults to `false`.
|
|
773
|
+
* @returns An Angular `ResolveFn<string>` to wire into a route's `title` field
|
|
774
|
+
* (or any other `resolve` slot — the return value isn't usually consumed).
|
|
545
775
|
*
|
|
546
|
-
*
|
|
776
|
+
* @example
|
|
777
|
+
* ```ts
|
|
778
|
+
* // Static title
|
|
779
|
+
* { path: 'about', component: AboutComponent, title: createTitle('About us') }
|
|
780
|
+
*
|
|
781
|
+
* // Factory using inject()
|
|
782
|
+
* {
|
|
783
|
+
* path: 'users/:id',
|
|
784
|
+
* component: UserComponent,
|
|
785
|
+
* title: createTitle(() => inject(ActivatedRoute).snapshot.params['id']),
|
|
786
|
+
* }
|
|
547
787
|
*
|
|
548
|
-
*
|
|
549
|
-
*
|
|
550
|
-
*
|
|
551
|
-
*
|
|
552
|
-
*
|
|
788
|
+
* // Reactive title from a signal store
|
|
789
|
+
* {
|
|
790
|
+
* path: 'dashboard',
|
|
791
|
+
* component: DashboardComponent,
|
|
792
|
+
* title: createTitle(() => {
|
|
793
|
+
* const user = inject(UserStore).current;
|
|
794
|
+
* return computed(() => `Dashboard – ${user()?.name ?? 'Guest'}`);
|
|
795
|
+
* }),
|
|
796
|
+
* }
|
|
797
|
+
* ```
|
|
553
798
|
*/
|
|
554
799
|
declare function createTitle(factoryOrValue: (() => string | (() => string)) | string, awaitValue?: boolean): ResolveFn<string>;
|
|
555
800
|
|