From 1ef4696cb7cf7aa2c0fe95e1a3786617c84e40cc Mon Sep 17 00:00:00 2001 From: Brady Isom Date: Fri, 18 Nov 2016 14:46:49 -0700 Subject: [PATCH] fix(upgrade): call ng1 lifecycle hooks (#12875) --- .../upgrade/src/upgrade_ng1_adapter.ts | 29 +++-- modules/@angular/upgrade/test/upgrade_spec.ts | 121 ++++++++++++++++++ 2 files changed, 141 insertions(+), 9 deletions(-) diff --git a/modules/@angular/upgrade/src/upgrade_ng1_adapter.ts b/modules/@angular/upgrade/src/upgrade_ng1_adapter.ts index a20397ee1f17a..f0a86d411ebba 100644 --- a/modules/@angular/upgrade/src/upgrade_ng1_adapter.ts +++ b/modules/@angular/upgrade/src/upgrade_ng1_adapter.ts @@ -49,7 +49,8 @@ export class UpgradeNg1ComponentAdapterBuilder { ], ngOnInit: function() { /* needs to be here for ng2 to properly detect it */ }, ngOnChanges: function() { /* needs to be here for ng2 to properly detect it */ }, - ngDoCheck: function() { /* needs to be here for ng2 to properly detect it */ } + ngDoCheck: function() { /* needs to be here for ng2 to properly detect it */ }, + ngOnDestroy: function() { /* needs to be here for ng2 to properly detect it */ }, }); } @@ -262,16 +263,18 @@ class UpgradeNg1ComponentAdapter implements OnInit, OnChanges, DoCheck { } ngOnChanges(changes: SimpleChanges) { - for (const name in changes) { - if ((changes).hasOwnProperty(name)) { - const change: SimpleChange = changes[name]; - this.setComponentProperty(name, change.currentValue); - } + const ng1Changes: any = {}; + Object.keys(changes).forEach(name => { + const change: SimpleChange = changes[name]; + this.setComponentProperty(name, change.currentValue); + ng1Changes[this.propertyMap[name]] = change; + }); + if (this.destinationObj.$onChanges) { + this.destinationObj.$onChanges(ng1Changes); } } - ngDoCheck(): number { - const count = 0; + ngDoCheck() { const destinationObj = this.destinationObj; const lastValues = this.checkLastValues; const checkProperties = this.checkProperties; @@ -287,7 +290,15 @@ class UpgradeNg1ComponentAdapter implements OnInit, OnChanges, DoCheck { } } } - return count; + if (this.destinationObj.$doCheck && this.directive.controller) { + this.destinationObj.$doCheck(); + } + } + + ngOnDestroy() { + if (this.destinationObj.$onDestroy && this.directive.controller) { + this.destinationObj.$onDestroy(); + } } setComponentProperty(name: string, value: any) { diff --git a/modules/@angular/upgrade/test/upgrade_spec.ts b/modules/@angular/upgrade/test/upgrade_spec.ts index 924f800cfdd8f..ca3d423beee16 100644 --- a/modules/@angular/upgrade/test/upgrade_spec.ts +++ b/modules/@angular/upgrade/test/upgrade_spec.ts @@ -922,6 +922,127 @@ export function main() { }); })); + it('should call $doCheck of components', async(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + const ng1Module = angular.module('ng1', []); + const valueToFind = '$doCheck'; + + let spy = jasmine.createSpy('doCheck'); + + const ng1 = { + bindings: {}, + template: '{{$ctrl.value}}', + controller: Class({ + constructor: function() {}, + $doCheck: function() { + this.value = valueToFind; + spy(); + } + }) + }; + ng1Module.component('ng1', ng1); + + const Ng2 = Component({selector: 'ng2', template: ''}).Class({ + constructor: function() {} + }); + + const Ng2Module = NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + schemas: [NO_ERRORS_SCHEMA], + }).Class({constructor: function() {}}); + + ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); + + const element = html(`
`); + adapter.bootstrap(element, ['ng1']).ready((ref) => { + expect(multiTrim(document.body.textContent)).toEqual(valueToFind); + expect(spy).toHaveBeenCalled(); + let count = spy.calls.count(); + setTimeout(() => { + expect(spy.calls.count()).toBeGreaterThan(count); + ref.dispose(); + }, 100); + }); + })); + + it('should call $onChanges of components', async(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + const ng1Module = angular.module('ng1', []); + const valueToFind = '$onChanges init'; + const valueToChange = '$onChanges changed'; + + const ng1 = { + bindings: {val: '<'}, + template: '{{$ctrl.value}}', + controller: Class({ + constructor: function() {}, + $onChanges: function(changes: any) { this.value = changes.val.currentValue; } + }) + }; + ng1Module.component('ng1', ng1); + + const Ng2 = Component({selector: 'ng2', template: ''}).Class({ + constructor: function() { this.val = valueToFind; }, + ngOnInit: function() { setTimeout(() => { this.val = valueToChange; }, 100); } + }); + + const Ng2Module = NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + schemas: [NO_ERRORS_SCHEMA], + }).Class({constructor: function() {}}); + + ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); + + const element = html(`
`); + adapter.bootstrap(element, ['ng1']).ready((ref) => { + expect(multiTrim(document.body.textContent)).toEqual(valueToFind); + setTimeout(() => { + expect(multiTrim(document.body.textContent)).toEqual(valueToChange); + ref.dispose(); + }, 200); + }); + })); + + it('should call $onDestroy of components', async(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + const ng1Module = angular.module('ng1', []); + + let spy = jasmine.createSpy('$onDestroy'); + + const ng1 = { + bindings: {}, + template: '
ng1
', + controller: function($rootScope: any) { this.$onDestroy = function() { spy(); }; } + }; + ng1Module.component('ng1', ng1); + + const Ng2 = Component({selector: 'ng2', template: ''}).Class({ + constructor: function() {} + }); + + const Ng2Module = NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + schemas: [NO_ERRORS_SCHEMA], + }).Class({constructor: function() {}}); + + ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); + + const element = html(`
`); + adapter.bootstrap(element, ['ng1']).ready((ref) => { + (ref.ng1RootScope).destroy = false; + setTimeout(() => { + (ref.ng1RootScope).destroy = true; + setTimeout(() => { + expect(spy).toHaveBeenCalled(); + ref.dispose(); + }, 100); + }, 100); + }); + })); + it('should bind input properties (<) of components', async(() => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); const ng1Module = angular.module('ng1', []);