Files
Andrew Scott ccb09b4558 fix(router): null/undefined routerLink should disable navigation (#43087)
The current behavior of `routerLink` for `null` and `undefined` inputs is to treat
the input the same as `[]`. This creates several unresolvable issues with
correctly disabling the links because `commands = []` does _not_ behave the same
as disabling a link. Instead, it navigates to the current page, but will also
clear any fragment and/or query params.

The new behavior of the `routerLink` input will be to completely disable navigation
for `null` and `undefined` inputs. For HTML Anchor elements, this will also mean
removing the `href` attribute.

Fixes #21457
Fixes #13980
Fixes #31154

BREAKING CHANGE:
Previously `null` and `undefined` inputs for `routerLink` were
equaivalent to empty string and there was no way to disable the link's
navigation.
In addition, the `href` is changed from a property `HostBinding()` to an
attribute binding (`HostBinding('attr.href')`). The effect of this
change is that `DebugElement.properties['href']` will now return the
`href` value returned by the native element which will be the full URL
rather than the internal value of the `RouterLink` `href` property.

PR Close #43087
2021-08-20 12:51:09 -07:00

105 lines
3.4 KiB
TypeScript

/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Component} from '@angular/core';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {Router} from '@angular/router';
import {RouterTestingModule} from '@angular/router/testing';
describe('RouterLink', () => {
it('does not modify tabindex if already set on non-anchor element', () => {
@Component({template: `<div [routerLink]="link" tabindex="1"></div>`})
class LinkComponent {
link: string|null|undefined = '/';
}
TestBed.configureTestingModule({imports: [RouterTestingModule], declarations: [LinkComponent]});
const fixture = TestBed.createComponent(LinkComponent);
fixture.detectChanges();
const link = fixture.debugElement.query(By.css('div')).nativeElement;
expect(link.tabIndex).toEqual(1);
fixture.nativeElement.link = null;
fixture.detectChanges();
expect(link.tabIndex).toEqual(1);
});
describe('on a non-anchor', () => {
@Component({template: `<div [routerLink]="link"></div>`})
class LinkComponent {
link: string|null|undefined = '/';
}
let fixture: ComponentFixture<LinkComponent>;
let link: HTMLDivElement;
let router: Router;
beforeEach(() => {
TestBed.configureTestingModule(
{imports: [RouterTestingModule], declarations: [LinkComponent]});
fixture = TestBed.createComponent(LinkComponent);
fixture.detectChanges();
link = fixture.debugElement.query(By.css('div')).nativeElement;
router = TestBed.inject(Router);
spyOn(router, 'navigateByUrl');
link.click();
expect(router.navigateByUrl).toHaveBeenCalled();
(router.navigateByUrl as jasmine.Spy).calls.reset();
});
it('null, removes tabIndex and does not navigate', () => {
fixture.componentInstance.link = null;
fixture.detectChanges();
expect(link.tabIndex).toEqual(-1);
link.click();
expect(router.navigateByUrl).not.toHaveBeenCalled();
});
it('undefined, removes tabIndex and does not navigate', () => {
fixture.componentInstance.link = undefined;
fixture.detectChanges();
expect(link.tabIndex).toEqual(-1);
link.click();
expect(router.navigateByUrl).not.toHaveBeenCalled();
});
});
describe('RouterLinkWithHref', () => {
@Component({template: `<a [routerLink]="link"></a>`})
class LinkComponent {
link: string|null|undefined = '/';
}
let fixture: ComponentFixture<LinkComponent>;
let link: HTMLAnchorElement;
beforeEach(() => {
TestBed.configureTestingModule(
{imports: [RouterTestingModule], declarations: [LinkComponent]});
fixture = TestBed.createComponent(LinkComponent);
fixture.detectChanges();
link = fixture.debugElement.query(By.css('a')).nativeElement;
});
it('null, removes href', () => {
expect(link.outerHTML).toContain('href');
fixture.componentInstance.link = null;
fixture.detectChanges();
expect(link.outerHTML).not.toContain('href');
});
it('undefined, removes href', () => {
expect(link.outerHTML).toContain('href');
fixture.componentInstance.link = undefined;
fixture.detectChanges();
expect(link.outerHTML).not.toContain('href');
});
});
});