From 0193be7c9b4c197ef986d90952296c151d9d2eb9 Mon Sep 17 00:00:00 2001 From: Georgios Kalpakas Date: Mon, 29 May 2017 20:52:50 +0300 Subject: [PATCH] feat(upgrade): fix support for `directive.link` in upgraded components (#17971) Although, pre- and post-linking functions are correctly called during directive linking, directives with `link.post` would throw an error. Interestingly, having `link.pre` only or defining `link: fn` (which is an alias for `link.post: fn`) would not throw. This commit removes this check and allows directives with pre- and/or post-linking functions to work. --- packages/upgrade/src/common/upgrade_helper.ts | 6 - .../integration/upgrade_component_spec.ts | 238 ++++++++++++++++++ 2 files changed, 238 insertions(+), 6 deletions(-) diff --git a/packages/upgrade/src/common/upgrade_helper.ts b/packages/upgrade/src/common/upgrade_helper.ts index d3d151fd2c..a58975e15c 100644 --- a/packages/upgrade/src/common/upgrade_helper.ts +++ b/packages/upgrade/src/common/upgrade_helper.ts @@ -90,12 +90,6 @@ export class UpgradeHelper { if (directive.terminal) this.notSupported('terminal'); if (directive.compile) this.notSupported('compile'); - const link = directive.link; - // QUESTION: why not support link.post? - if (typeof link == 'object') { - if (link.post) this.notSupported('link.post'); - } - return directive; } diff --git a/packages/upgrade/test/static/integration/upgrade_component_spec.ts b/packages/upgrade/test/static/integration/upgrade_component_spec.ts index 988aa5ca8e..a2d31286a3 100644 --- a/packages/upgrade/test/static/integration/upgrade_component_spec.ts +++ b/packages/upgrade/test/static/integration/upgrade_component_spec.ts @@ -1069,6 +1069,244 @@ export function main() { })); }); + describe('linking', () => { + it('should run the pre-linking after instantiating the controller', async(() => { + const log: string[] = []; + + // Define `ng1Directive` + const ng1Directive: angular.IDirective = { + template: '', + link: {pre: () => log.push('ng1-pre')}, + controller: class {constructor() { log.push('ng1-ctrl'); }} + }; + + // Define `Ng1ComponentFacade` + @Directive({selector: 'ng1'}) + class Ng1ComponentFacade extends UpgradeComponent { + constructor(elementRef: ElementRef, injector: Injector) { + super('ng1', elementRef, injector); + } + } + + // Define `Ng2Component` + @Component({selector: 'ng2', template: ''}) + class Ng2Component { + } + + // Define `ng1Module` + const ng1Module = angular.module('ng1', []) + .directive('ng1', () => ng1Directive) + .directive('ng2', downgradeComponent({component: Ng2Component})); + + // Define `Ng2Module` + @NgModule({ + imports: [BrowserModule, UpgradeModule], + declarations: [Ng1ComponentFacade, Ng2Component], + entryComponents: [Ng2Component], + }) + class Ng2Module { + ngDoBootstrap() {} + } + + // Bootstrap + const element = html(``); + + bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => { + expect(log).toEqual(['ng1-ctrl', 'ng1-pre']); + }); + })); + + it('should run the pre-linking function before linking', async(() => { + const log: string[] = []; + + // Define `ng1Directive` + const ng1DirectiveA: angular.IDirective = { + template: '', + link: {pre: () => log.push('ng1A-pre')} + }; + + const ng1DirectiveB: angular.IDirective = {link: () => log.push('ng1B-post')}; + + // Define `Ng1ComponentAFacade` + @Directive({selector: 'ng1A'}) + class Ng1ComponentAFacade extends UpgradeComponent { + constructor(elementRef: ElementRef, injector: Injector) { + super('ng1A', elementRef, injector); + } + } + + // Define `Ng2Component` + @Component({selector: 'ng2', template: ''}) + class Ng2Component { + } + + // Define `ng1Module` + const ng1Module = angular.module('ng1', []) + .directive('ng1A', () => ng1DirectiveA) + .directive('ng1B', () => ng1DirectiveB) + .directive('ng2', downgradeComponent({component: Ng2Component})); + + // Define `Ng2Module` + @NgModule({ + imports: [BrowserModule, UpgradeModule], + declarations: [Ng1ComponentAFacade, Ng2Component], + entryComponents: [Ng2Component], + }) + class Ng2Module { + ngDoBootstrap() {} + } + + // Bootstrap + const element = html(``); + + bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => { + expect(log).toEqual(['ng1A-pre', 'ng1B-post']); + }); + })); + + it('should run the post-linking function after linking (link: object)', async(() => { + const log: string[] = []; + + // Define `ng1Directive` + const ng1DirectiveA: angular.IDirective = { + template: '', + link: {post: () => log.push('ng1A-post')} + }; + + const ng1DirectiveB: angular.IDirective = {link: () => log.push('ng1B-post')}; + + // Define `Ng1ComponentAFacade` + @Directive({selector: 'ng1A'}) + class Ng1ComponentAFacade extends UpgradeComponent { + constructor(elementRef: ElementRef, injector: Injector) { + super('ng1A', elementRef, injector); + } + } + + // Define `Ng2Component` + @Component({selector: 'ng2', template: ''}) + class Ng2Component { + } + + // Define `ng1Module` + const ng1Module = angular.module('ng1', []) + .directive('ng1A', () => ng1DirectiveA) + .directive('ng1B', () => ng1DirectiveB) + .directive('ng2', downgradeComponent({component: Ng2Component})); + + // Define `Ng2Module` + @NgModule({ + imports: [BrowserModule, UpgradeModule], + declarations: [Ng1ComponentAFacade, Ng2Component], + entryComponents: [Ng2Component], + }) + class Ng2Module { + ngDoBootstrap() {} + } + + // Bootstrap + const element = html(``); + + bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => { + expect(log).toEqual(['ng1B-post', 'ng1A-post']); + }); + })); + + it('should run the post-linking function after linking (link: function)', async(() => { + const log: string[] = []; + + // Define `ng1Directive` + const ng1DirectiveA: angular.IDirective = { + template: '', + link: () => log.push('ng1A-post') + }; + + const ng1DirectiveB: angular.IDirective = {link: () => log.push('ng1B-post')}; + + // Define `Ng1ComponentAFacade` + @Directive({selector: 'ng1A'}) + class Ng1ComponentAFacade extends UpgradeComponent { + constructor(elementRef: ElementRef, injector: Injector) { + super('ng1A', elementRef, injector); + } + } + + // Define `Ng2Component` + @Component({selector: 'ng2', template: ''}) + class Ng2Component { + } + + // Define `ng1Module` + const ng1Module = angular.module('ng1', []) + .directive('ng1A', () => ng1DirectiveA) + .directive('ng1B', () => ng1DirectiveB) + .directive('ng2', downgradeComponent({component: Ng2Component})); + + // Define `Ng2Module` + @NgModule({ + imports: [BrowserModule, UpgradeModule], + declarations: [Ng1ComponentAFacade, Ng2Component], + entryComponents: [Ng2Component], + }) + class Ng2Module { + ngDoBootstrap() {} + } + + // Bootstrap + const element = html(``); + + bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => { + expect(log).toEqual(['ng1B-post', 'ng1A-post']); + }); + })); + + it('should run the post-linking function before `$postLink`', async(() => { + const log: string[] = []; + + // Define `ng1Directive` + const ng1Directive: angular.IDirective = { + template: '', + link: () => log.push('ng1-post'), + controller: class {$postLink() { log.push('ng1-$post'); }} + }; + + // Define `Ng1ComponentFacade` + @Directive({selector: 'ng1'}) + class Ng1ComponentFacade extends UpgradeComponent { + constructor(elementRef: ElementRef, injector: Injector) { + super('ng1', elementRef, injector); + } + } + + // Define `Ng2Component` + @Component({selector: 'ng2', template: ''}) + class Ng2Component { + } + + // Define `ng1Module` + const ng1Module = angular.module('ng1', []) + .directive('ng1', () => ng1Directive) + .directive('ng2', downgradeComponent({component: Ng2Component})); + + // Define `Ng2Module` + @NgModule({ + imports: [BrowserModule, UpgradeModule], + declarations: [Ng1ComponentFacade, Ng2Component], + entryComponents: [Ng2Component], + }) + class Ng2Module { + ngDoBootstrap() {} + } + + // Bootstrap + const element = html(``); + + bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => { + expect(log).toEqual(['ng1-post', 'ng1-$post']); + }); + })); + }); + describe('controller', () => { it('should support `controllerAs`', async(() => { // Define `ng1Directive`