Skip to content

Commit 16ef770

Browse files
JeanMechedylhunn
authored andcommitted
fix(router): Handle routerLink directive on svg anchors. (#48857)
On svgs, the tagNames are lowercase even for non-svg related tags like `a`. fixes #48854 PR Close #48857
1 parent 6e0e94d commit 16ef770

File tree

2 files changed

+71
-49
lines changed

2 files changed

+71
-49
lines changed

packages/router/src/directives/router_link.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,8 @@ export class RouterLink implements OnChanges, OnDestroy {
190190
@Attribute('tabindex') private readonly tabIndexAttribute: string|null|undefined,
191191
private readonly renderer: Renderer2, private readonly el: ElementRef,
192192
private locationStrategy?: LocationStrategy) {
193-
const tagName = el.nativeElement.tagName;
194-
this.isAnchorElement = tagName === 'A' || tagName === 'AREA';
193+
const tagName = el.nativeElement.tagName?.toLowerCase();
194+
this.isAnchorElement = tagName === 'a' || tagName === 'area';
195195

196196
if (this.isAnchorElement) {
197197
this.subscription = router.events.subscribe((s: Event) => {

packages/router/test/router_link_spec.ts

Lines changed: 69 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,10 @@ describe('RouterLink', () => {
3939
let router: Router;
4040

4141
beforeEach(() => {
42-
TestBed.configureTestingModule(
43-
{imports: [RouterTestingModule], declarations: [LinkComponent]});
42+
TestBed.configureTestingModule({
43+
imports: [RouterTestingModule],
44+
declarations: [LinkComponent],
45+
});
4446
fixture = TestBed.createComponent(LinkComponent);
4547
fixture.detectChanges();
4648
link = fixture.debugElement.query(By.css('div')).nativeElement;
@@ -93,56 +95,76 @@ describe('RouterLink', () => {
9395
});
9496
});
9597

96-
describe('RouterLink for elements with `href` attributes', () => {
97-
@Component({template: `<a [routerLink]="link"></a>`})
98-
class LinkComponent {
99-
link: string|null|undefined = '/';
100-
}
101-
let fixture: ComponentFixture<LinkComponent>;
102-
let link: HTMLAnchorElement;
103-
104-
beforeEach(() => {
105-
TestBed.configureTestingModule(
106-
{imports: [RouterTestingModule], declarations: [LinkComponent]});
107-
fixture = TestBed.createComponent(LinkComponent);
108-
fixture.detectChanges();
109-
link = fixture.debugElement.query(By.css('a')).nativeElement;
98+
describe('on an anchor', () => {
99+
describe('RouterLink for elements with `href` attributes', () => {
100+
@Component({template: `<a [routerLink]="link"></a>`})
101+
class LinkComponent {
102+
link: string|null|undefined = '/';
103+
}
104+
let fixture: ComponentFixture<LinkComponent>;
105+
let link: HTMLAnchorElement;
106+
107+
beforeEach(() => {
108+
TestBed.configureTestingModule({
109+
imports: [RouterTestingModule],
110+
declarations: [LinkComponent],
111+
});
112+
fixture = TestBed.createComponent(LinkComponent);
113+
fixture.detectChanges();
114+
link = fixture.debugElement.query(By.css('a')).nativeElement;
115+
});
116+
117+
it('null, removes href', () => {
118+
expect(link.outerHTML).toContain('href');
119+
fixture.componentInstance.link = null;
120+
fixture.detectChanges();
121+
expect(link.outerHTML).not.toContain('href');
122+
});
123+
124+
it('undefined, removes href', () => {
125+
expect(link.outerHTML).toContain('href');
126+
fixture.componentInstance.link = undefined;
127+
fixture.detectChanges();
128+
expect(link.outerHTML).not.toContain('href');
129+
});
130+
131+
it('should coerce boolean input values', () => {
132+
const dir = fixture.debugElement.query(By.directive(RouterLink)).injector.get(RouterLink);
133+
134+
for (const truthy of [true, '', 'true', 'anything']) {
135+
dir.preserveFragment = truthy;
136+
dir.skipLocationChange = truthy;
137+
dir.replaceUrl = truthy;
138+
expect(dir.preserveFragment).toBeTrue();
139+
expect(dir.skipLocationChange).toBeTrue();
140+
expect(dir.replaceUrl).toBeTrue();
141+
}
142+
143+
for (const falsy of [false, null, undefined, 'false']) {
144+
dir.preserveFragment = falsy;
145+
dir.skipLocationChange = falsy;
146+
dir.replaceUrl = falsy;
147+
expect(dir.preserveFragment).toBeFalse();
148+
expect(dir.skipLocationChange).toBeFalse();
149+
expect(dir.replaceUrl).toBeFalse();
150+
}
151+
});
110152
});
111153

112-
it('null, removes href', () => {
113-
expect(link.outerHTML).toContain('href');
114-
fixture.componentInstance.link = null;
115-
fixture.detectChanges();
116-
expect(link.outerHTML).not.toContain('href');
117-
});
154+
it('should handle routerLink in svg templates', () => {
155+
@Component({template: `<svg><a routerLink="test"></a></svg>`})
156+
class LinkComponent {
157+
}
118158

119-
it('undefined, removes href', () => {
120-
expect(link.outerHTML).toContain('href');
121-
fixture.componentInstance.link = undefined;
159+
TestBed.configureTestingModule({
160+
imports: [RouterTestingModule],
161+
declarations: [LinkComponent],
162+
});
163+
const fixture = TestBed.createComponent(LinkComponent);
122164
fixture.detectChanges();
123-
expect(link.outerHTML).not.toContain('href');
124-
});
125-
126-
it('should coerce boolean input values', () => {
127-
const dir = fixture.debugElement.query(By.directive(RouterLink)).injector.get(RouterLink);
128-
129-
for (const truthy of [true, '', 'true', 'anything']) {
130-
dir.preserveFragment = truthy;
131-
dir.skipLocationChange = truthy;
132-
dir.replaceUrl = truthy;
133-
expect(dir.preserveFragment).toBeTrue();
134-
expect(dir.skipLocationChange).toBeTrue();
135-
expect(dir.replaceUrl).toBeTrue();
136-
}
165+
const link = fixture.debugElement.query(By.css('a')).nativeElement;
137166

138-
for (const falsy of [false, null, undefined, 'false']) {
139-
dir.preserveFragment = falsy;
140-
dir.skipLocationChange = falsy;
141-
dir.replaceUrl = falsy;
142-
expect(dir.preserveFragment).toBeFalse();
143-
expect(dir.skipLocationChange).toBeFalse();
144-
expect(dir.replaceUrl).toBeFalse();
145-
}
167+
expect(link.outerHTML).toContain('href');
146168
});
147169
});
148170
});

0 commit comments

Comments
 (0)