Skip to content

Commit e44839b

Browse files
atscottkirjs
authored andcommitted
feat(router): Add standalone function to create a comptued for isActive
This change deprecates `Router.isActive` in favor of a function that creates a computed which tracks whether a given url/UrlTree is active, following changes in the router state. `Router.isActive` contributes ~1333b to the bundle size, but is only used by developers who use the API directly or who use `RouterLinkActive`. It should not contribute to bundle sizes of applications that do not use this functionality.
1 parent 51ed487 commit e44839b

File tree

6 files changed

+50
-8
lines changed

6 files changed

+50
-8
lines changed

goldens/public-api/router/index.api.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,9 @@ export interface InMemoryScrollingOptions {
369369
scrollPositionRestoration?: 'disabled' | 'enabled' | 'top';
370370
}
371371

372+
// @public
373+
export function isActive(url: string | UrlTree, router: Router, matchOptions: IsActiveMatchOptions): Signal<boolean>;
374+
372375
// @public
373376
export interface IsActiveMatchOptions {
374377
fragment: 'exact' | 'ignored';
@@ -722,6 +725,7 @@ export class Router {
722725
initialNavigation(): void;
723726
// @deprecated
724727
isActive(url: string | UrlTree, exact: boolean): boolean;
728+
// @deprecated
725729
isActive(url: string | UrlTree, matchOptions: IsActiveMatchOptions): boolean;
726730
get lastSuccessfulNavigation(): Signal<Navigation | null>;
727731
navigate(commands: readonly any[], extras?: NavigationExtras): Promise<boolean>;

packages/core/test/bundling/router/bundle.golden_symbols.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
"COMPLETE_NOTIFICATION",
4242
"COMPONENT_REGEX",
4343
"COMPONENT_VARIABLE",
44+
"COMPUTED_NODE",
45+
"COMPUTING",
4446
"CONTAINER_HEADER_OFFSET",
4547
"CONTENT_ATTR",
4648
"CONTEXT",
@@ -96,6 +98,7 @@
9698
"ENABLE_ROOT_COMPONENT_BOOTSTRAP",
9799
"ENVIRONMENT",
98100
"ENVIRONMENT_INITIALIZER",
101+
"ERRORED",
99102
"EVENT_MANAGER_PLUGINS",
100103
"EffectRefImpl",
101104
"EffectScheduler",
@@ -306,6 +309,7 @@
306309
"TracingService",
307310
"Tree",
308311
"TreeNode",
312+
"UNSET",
309313
"USE_VALUE",
310314
"UnsubscriptionError",
311315
"UrlHandlingStrategy",
@@ -452,6 +456,7 @@
452456
"compare",
453457
"computeNavigation",
454458
"computeStaticStyling",
459+
"computed",
455460
"concat",
456461
"concatAll",
457462
"concatMap",
@@ -480,6 +485,7 @@
480485
"createAndRenderEmbeddedLView",
481486
"createChildrenForEmptyPaths",
482487
"createComponentLView",
488+
"createComputed",
483489
"createContainerRef",
484490
"createContentQuery",
485491
"createDirectivesInstances",
@@ -779,6 +785,7 @@
779785
"interpolation1",
780786
"invokeDirectivesHostBindings",
781787
"invokeHostBindingsInCreationMode",
788+
"isActive",
782789
"isActiveMatchOptions",
783790
"isAngularZoneProperty",
784791
"isAnimationEventType",

packages/router/src/directives/router_link_active.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,14 @@ import {
2121
QueryList,
2222
Renderer2,
2323
SimpleChanges,
24+
untracked,
2425
} from '@angular/core';
2526
import {from, of, Subscription} from 'rxjs';
2627
import {mergeAll} from 'rxjs/operators';
2728

2829
import {Event, NavigationEnd} from '../events';
29-
import {Router} from '../router';
30-
import {IsActiveMatchOptions} from '../url_tree';
30+
import {exactMatchOptions, Router, subsetMatchOptions} from '../router';
31+
import {isActive, IsActiveMatchOptions} from '../url_tree';
3132

3233
import {RouterLink} from './router_link';
3334

@@ -246,15 +247,16 @@ export class RouterLinkActive implements OnChanges, OnDestroy, AfterContentInit
246247
}
247248

248249
private isLinkActive(router: Router): (link: RouterLink) => boolean {
249-
const options: boolean | IsActiveMatchOptions = isActiveMatchOptions(
250-
this.routerLinkActiveOptions,
251-
)
250+
const options: IsActiveMatchOptions = isActiveMatchOptions(this.routerLinkActiveOptions)
252251
? this.routerLinkActiveOptions
253252
: // While the types should disallow `undefined` here, it's possible without strict inputs
254-
this.routerLinkActiveOptions.exact || false;
253+
(this.routerLinkActiveOptions.exact ?? false)
254+
? {...exactMatchOptions}
255+
: {...subsetMatchOptions};
256+
255257
return (link: RouterLink) => {
256258
const urlTree = link.urlTree;
257-
return urlTree ? router.isActive(urlTree, options) : false;
259+
return urlTree ? untracked(isActive(urlTree, router, options)) : false;
258260
};
259261
}
260262

packages/router/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ export {
137137
UrlSegmentGroup,
138138
UrlSerializer,
139139
UrlTree,
140+
isActive,
140141
} from './url_tree';
141142
export {
142143
mapToCanActivate,

packages/router/src/router.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,10 +629,16 @@ export class Router {
629629
isActive(url: string | UrlTree, exact: boolean): boolean;
630630
/**
631631
* Returns whether the url is activated.
632+
* @deprecated 21.1 - Use the `isActive` function instead.
633+
* @see {@link isActive}
632634
*/
633635
isActive(url: string | UrlTree, matchOptions: IsActiveMatchOptions): boolean;
634636
/** @internal */
635637
isActive(url: string | UrlTree, matchOptions: boolean | IsActiveMatchOptions): boolean;
638+
/**
639+
* @deprecated 21.1 - Use the `isActive` function instead.
640+
* @see {@link isActive}
641+
*/
636642
isActive(url: string | UrlTree, matchOptions: boolean | IsActiveMatchOptions): boolean {
637643
let options: IsActiveMatchOptions;
638644
if (matchOptions === true) {

packages/router/src/url_tree.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import {Injectable, ɵRuntimeError as RuntimeError} from '@angular/core';
9+
import {computed, Injectable, ɵRuntimeError as RuntimeError, Signal} from '@angular/core';
1010

1111
import {RuntimeErrorCode} from './errors';
1212
import {convertToParamMap, ParamMap, Params, PRIMARY_OUTLET} from './shared';
1313
import {equalArraysOrString, shallowEqual} from './utils/collection';
14+
import type {Router} from './router';
1415

1516
/**
1617
* A set of options which specify how to determine if a `UrlTree` is active, given the `UrlTree`
@@ -79,6 +80,27 @@ const paramCompareMap: Record<ParamMatchOptions, ParamCompareFn> = {
7980
'ignored': () => true,
8081
};
8182

83+
/**
84+
* Returns a computed signal of whether the given url is activated in the Router.
85+
*
86+
* As the router state changes, the signal will update to reflect whether the url is active.
87+
* @publicApi 21.1
88+
*/
89+
export function isActive(
90+
url: string | UrlTree,
91+
router: Router,
92+
matchOptions: IsActiveMatchOptions,
93+
): Signal<boolean> {
94+
const urlTree = url instanceof UrlTree ? url : router.parseUrl(url);
95+
return computed(() =>
96+
containsTree(
97+
router.lastSuccessfulNavigation()?.finalUrl ?? new UrlTree(),
98+
urlTree,
99+
matchOptions,
100+
),
101+
);
102+
}
103+
82104
export function containsTree(
83105
container: UrlTree,
84106
containee: UrlTree,

0 commit comments

Comments
 (0)