Skip to content

Commit 41cd4a6

Browse files
atscottkirjs
authored andcommitted
fix(router): Fix RouterLink href not updating with queryParamsHandling
There was a bug introduced in #60875. While RouterLink doesn't necessarily depend on Router state, its link can change when navigations cause the `ActivatedRoute` paths to change. It is difficult to determine which segments the link depends on. Luckily, the commit had flawed logic: ``` !this.queryParamsHandling && !dependsOnRouterState(this.options?.defaultQueryParamsHandling); ``` The subscription gets created whenever `queryParamsHandling` was not defined, or it was non-default. This is pretty much all scenarios since nobody is likely to set it explicitly to the default value. In addition, the `click` handler recomputes the tree because `urlTree` is a getter that does the computation. This commit effectively rolls back #60875 (cherry picked from commit bcef77d)
1 parent ae1dc16 commit 41cd4a6

2 files changed

Lines changed: 21 additions & 21 deletions

File tree

packages/router/src/directives/router_link.ts

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -254,27 +254,14 @@ export class RouterLink implements OnChanges, OnDestroy {
254254
)
255255
);
256256

257-
if (!this.isAnchorElement) {
258-
this.subscribeToNavigationEventsIfNecessary();
259-
} else {
257+
if (this.isAnchorElement) {
260258
this.setTabIndexIfNotOnNativeEl('0');
259+
this.subscribeToNavigationEventsIfNecessary();
261260
}
262261
}
263262

264263
private subscribeToNavigationEventsIfNecessary() {
265-
if (this.subscription !== undefined || !this.isAnchorElement) {
266-
return;
267-
}
268-
269-
// preserving fragment in router state
270-
let createSubcription = this.preserveFragment;
271-
// preserving or merging with query params in router state
272-
const dependsOnRouterState = (handling?: QueryParamsHandling | null) =>
273-
handling === 'merge' || handling === 'preserve';
274-
createSubcription ||= dependsOnRouterState(this.queryParamsHandling);
275-
createSubcription ||=
276-
!this.queryParamsHandling && !dependsOnRouterState(this.options?.defaultQueryParamsHandling);
277-
if (!createSubcription) {
264+
if (this.subscription !== undefined) {
278265
return;
279266
}
280267

@@ -339,7 +326,6 @@ export class RouterLink implements OnChanges, OnDestroy {
339326
}
340327
if (this.isAnchorElement) {
341328
this.updateHref();
342-
this.subscribeToNavigationEventsIfNecessary();
343329
}
344330
// This is subscribed to by `RouterLinkActive` so that it knows to update when there are changes
345331
// to the RouterLinks it's tracking.

packages/router/test/router_link_spec.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,9 @@ import {Component, inject, signal, provideZonelessChangeDetection} from '@angula
1010
import {ComponentFixture, TestBed} from '@angular/core/testing';
1111
import {By} from '@angular/platform-browser';
1212
import {Router, RouterLink, RouterModule, provideRouter} from '../index';
13+
import {RouterTestingHarness} from '../testing';
1314

1415
describe('RouterLink', () => {
15-
beforeEach(() => {
16-
TestBed.configureTestingModule({providers: [provideZonelessChangeDetection()]});
17-
});
18-
1916
it('does not modify tabindex if already set on non-anchor element', async () => {
2017
@Component({
2118
template: `<div [routerLink]="link" tabindex="1"></div>`,
@@ -315,4 +312,21 @@ describe('RouterLink', () => {
315312
const fixture = TestBed.createComponent(WithUrlTree);
316313
expect(() => fixture.changeDetectorRef.detectChanges()).toThrow();
317314
});
315+
316+
it('correctly updates when relativeTo segments change', async () => {
317+
@Component({
318+
template: `<a [routerLink]="['./child']" queryParamsHandling="'replace'">link</a>`,
319+
imports: [RouterLink],
320+
})
321+
class WithLink {}
322+
TestBed.configureTestingModule({
323+
providers: [provideRouter([{path: '**', component: WithLink}])],
324+
});
325+
326+
const harness = await RouterTestingHarness.create('/initial');
327+
const anchor = harness.fixture.nativeElement.querySelector('a');
328+
expect(anchor.getAttribute('href')).toBe('/initial/child');
329+
await harness.navigateByUrl('/different');
330+
expect(anchor.getAttribute('href')).toBe('/different/child');
331+
});
318332
});

0 commit comments

Comments
 (0)