diff --git a/goldens/public-api/router/index.md b/goldens/public-api/router/index.md index 9700f6315de..8e57cbf64cb 100644 --- a/goldens/public-api/router/index.md +++ b/goldens/public-api/router/index.md @@ -782,6 +782,7 @@ class RouterLink implements OnChanges, OnDestroy { constructor(router: Router, route: ActivatedRoute, tabIndexAttribute: string | null | undefined, renderer: Renderer2, el: ElementRef, locationStrategy?: LocationStrategy | undefined); fragment?: string; href: string | null; + info?: unknown; // (undocumented) static ngAcceptInputType_preserveFragment: unknown; // (undocumented) @@ -808,7 +809,7 @@ class RouterLink implements OnChanges, OnDestroy { // (undocumented) get urlTree(): UrlTree | null; // (undocumented) - static ɵdir: i0.ɵɵDirectiveDeclaration; + static ɵdir: i0.ɵɵDirectiveDeclaration; // (undocumented) static ɵfac: i0.ɵɵFactoryDeclaration; } diff --git a/packages/router/src/directives/router_link.ts b/packages/router/src/directives/router_link.ts index 274e8eccb30..2a1f09bfd4b 100644 --- a/packages/router/src/directives/router_link.ts +++ b/packages/router/src/directives/router_link.ts @@ -160,6 +160,13 @@ export class RouterLink implements OnChanges, OnDestroy { * @see {@link Router#navigateByUrl} */ @Input() state?: {[k: string]: any}; + /** + * Passed to {@link Router#navigateByUrl} as part of the + * `NavigationBehaviorOptions`. + * @see {@link NavigationBehaviorOptions#info} + * @see {@link Router#navigateByUrl} + */ + @Input() info?: unknown; /** * Passed to {@link Router#createUrlTree} as part of the * `UrlCreationOptions`. @@ -287,6 +294,7 @@ export class RouterLink implements OnChanges, OnDestroy { skipLocationChange: this.skipLocationChange, replaceUrl: this.replaceUrl, state: this.state, + info: this.info, }; this.router.navigateByUrl(this.urlTree, extras); diff --git a/packages/router/test/integration.spec.ts b/packages/router/test/integration.spec.ts index 72af150a835..bc265f0d56d 100644 --- a/packages/router/test/integration.spec.ts +++ b/packages/router/test/integration.spec.ts @@ -7,13 +7,13 @@ */ import {CommonModule, HashLocationStrategy, Location, LocationStrategy, PlatformLocation, PopStateEvent} from '@angular/common'; -import {ChangeDetectionStrategy, Component, EnvironmentInjector, inject as coreInject, Inject, Injectable, InjectionToken, NgModule, NgModuleRef, NgZone, OnDestroy, QueryList, Type, ViewChild, ViewChildren, ɵConsole as Console, ɵNoopNgZone as NoopNgZone} from '@angular/core'; +import {ApplicationRef, ChangeDetectionStrategy, Component, EnvironmentInjector, inject as coreInject, Inject, Injectable, InjectionToken, NgModule, NgModuleRef, NgZone, OnDestroy, QueryList, Type, ViewChild, ViewChildren, ɵConsole as Console, ɵNoopNgZone as NoopNgZone} from '@angular/core'; import {ComponentFixture, fakeAsync, inject, TestBed, tick} from '@angular/core/testing'; import {By} from '@angular/platform-browser/src/dom/debug/by'; import {expect} from '@angular/platform-browser/testing/src/matchers'; import {ActivatedRoute, ActivatedRouteSnapshot, ActivationEnd, ActivationStart, ChildActivationEnd, ChildActivationStart, DefaultUrlSerializer, DetachedRouteHandle, Event, GuardsCheckEnd, GuardsCheckStart, Navigation, NavigationCancel, NavigationCancellationCode, NavigationEnd, NavigationError, NavigationSkipped, NavigationStart, ParamMap, Params, PreloadAllModules, PreloadingStrategy, PRIMARY_OUTLET, ResolveEnd, ResolveStart, RouteConfigLoadEnd, RouteConfigLoadStart, Router, RouteReuseStrategy, RouterEvent, RouterLink, RouterLinkActive, RouterModule, RouterOutlet, RouterPreloader, RouterStateSnapshot, RoutesRecognized, RunGuardsAndResolvers, UrlHandlingStrategy, UrlSegment, UrlSegmentGroup, UrlSerializer, UrlTree} from '@angular/router'; import {RouterTestingHarness} from '@angular/router/testing'; -import {concat, EMPTY, Observable, Observer, of, Subscription} from 'rxjs'; +import {concat, EMPTY, firstValueFrom, Observable, Observer, of, Subscription} from 'rxjs'; import {delay, filter, first, last, map, mapTo, takeWhile, tap} from 'rxjs/operators'; import {CanActivateChildFn, CanActivateFn, CanMatchFn, Data, ResolveFn} from '../src/models'; @@ -158,6 +158,37 @@ describe('Integration', () => { expect(observedInfo).toEqual('navigation info'); }); + it('should set transient navigation info for routerlink', async () => { + let observedInfo: unknown; + const router = TestBed.inject(Router); + router.resetConfig([ + { + path: 'simple', + component: SimpleCmp, + canActivate: [() => { + observedInfo = coreInject(Router).getCurrentNavigation()?.extras?.info; + return true; + }] + }, + ]); + @Component({ + standalone: true, + imports: [RouterLink], + template: `` + }) + class App { + } + + const fixture = TestBed.createComponent(App); + fixture.autoDetectChanges(); + const anchor = fixture.nativeElement.querySelector('a'); + anchor.click(); + await fixture.whenStable(); + + // An example use-case might be to pass the clicked link along with the navigation information + expect(observedInfo).toBeInstanceOf(HTMLAnchorElement); + }); + it('should make transient navigation info available in redirect', async () => { let observedInfo: unknown; const router = TestBed.inject(Router);