From 174334dec36ff12d4edf364cd0756fba936c69f8 Mon Sep 17 00:00:00 2001 From: Dzmitry Shylovich Date: Wed, 28 Dec 2016 00:45:16 +0300 Subject: [PATCH] fix(router): routerLink support of null/undefined (#13380) Closes #6971 --- .../@angular/router/src/create_url_tree.ts | 12 ++++++----- .../router/src/directives/router_link.ts | 18 ++++++++--------- .../@angular/router/test/integration.spec.ts | 20 +++++++++++++++++++ 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/modules/@angular/router/src/create_url_tree.ts b/modules/@angular/router/src/create_url_tree.ts index ed873bbbef..91ae21072c 100644 --- a/modules/@angular/router/src/create_url_tree.ts +++ b/modules/@angular/router/src/create_url_tree.ts @@ -34,7 +34,7 @@ export function createUrlTree( } function isMatrixParams(command: any): boolean { - return typeof command === 'object' && !command.outlets && !command.segmentPath; + return typeof command === 'object' && command != null && !command.outlets && !command.segmentPath; } function tree( @@ -70,7 +70,7 @@ class Navigation { throw new Error('Root segment cannot have matrix parameters'); } - const cmdWithOutlet = commands.find(c => typeof c === 'object' && c.outlets); + const cmdWithOutlet = commands.find(c => typeof c === 'object' && c != null && c.outlets); if (cmdWithOutlet && cmdWithOutlet !== last(commands)) { throw new Error('{outlets:{}} has to be the last command'); } @@ -91,7 +91,7 @@ function computeNavigation(commands: any[]): Navigation { let isAbsolute = false; const res: any[] = commands.reduce((res, cmd, cmdIdx) => { - if (typeof cmd === 'object') { + if (typeof cmd === 'object' && cmd != null) { if (cmd.outlets) { const outlets: {[k: string]: any} = {}; forEach(cmd.outlets, (commands: any, name: string) => { @@ -169,7 +169,9 @@ function createPositionApplyingDoubleDots( } function getPath(command: any): any { - if (typeof command === 'object' && command.outlets) return command.outlets[PRIMARY_OUTLET]; + if (typeof command === 'object' && command != null && command.outlets) { + return command.outlets[PRIMARY_OUTLET]; + } return `${command}`; } @@ -306,4 +308,4 @@ function stringify(params: {[key: string]: any}): {[key: string]: string} { function compare(path: string, params: {[key: string]: any}, segment: UrlSegment): boolean { return path == segment.path && shallowEqual(params, segment.parameters); -} \ No newline at end of file +} diff --git a/modules/@angular/router/src/directives/router_link.ts b/modules/@angular/router/src/directives/router_link.ts index d2fe867e19..3b54226c6d 100644 --- a/modules/@angular/router/src/directives/router_link.ts +++ b/modules/@angular/router/src/directives/router_link.ts @@ -98,15 +98,15 @@ export class RouterLink { } @Input() - set routerLink(data: any[]|string) { - if (Array.isArray(data)) { - this.commands = data; + set routerLink(commands: any[]|string) { + if (commands != null) { + this.commands = Array.isArray(commands) ? commands : [commands]; } else { - this.commands = [data]; + this.commands = []; } } - @HostListener('click', []) + @HostListener('click') onClick(): boolean { const extras = { skipLocationChange: attrBoolValue(this.skipLocationChange), @@ -163,11 +163,11 @@ export class RouterLinkWithHref implements OnChanges, OnDestroy { } @Input() - set routerLink(data: any[]|string) { - if (Array.isArray(data)) { - this.commands = data; + set routerLink(commands: any[]|string) { + if (commands != null) { + this.commands = Array.isArray(commands) ? commands : [commands]; } else { - this.commands = [data]; + this.commands = []; } } diff --git a/modules/@angular/router/test/integration.spec.ts b/modules/@angular/router/test/integration.spec.ts index ca29193f81..7f3d86b0af 100644 --- a/modules/@angular/router/test/integration.spec.ts +++ b/modules/@angular/router/test/integration.spec.ts @@ -1022,6 +1022,26 @@ describe('Integration', () => { expect(native.getAttribute('href')).toEqual('/home'); })); + it('should not throw when commands is null', fakeAsync(() => { + @Component({ + selector: 'someCmp', + template: + `Link` + }) + class CmpWithLink { + } + + TestBed.configureTestingModule({declarations: [CmpWithLink]}); + const router: Router = TestBed.get(Router); + + let fixture: ComponentFixture = createRoot(router, CmpWithLink); + router.resetConfig([{path: 'home', component: SimpleCmp}]); + const anchor = fixture.nativeElement.querySelector('a'); + const button = fixture.nativeElement.querySelector('button'); + expect(() => anchor.click()).not.toThrow(); + expect(() => button.click()).not.toThrow(); + })); + it('should update hrefs when query params or fragment change', fakeAsync(() => { @Component({