From ba6b69a0cce62e701583ebe91086be569e4aad43 Mon Sep 17 00:00:00 2001 From: Michael Giambalvo Date: Tue, 14 Mar 2017 16:22:04 -0700 Subject: [PATCH] fix(upgrade): Run digest cycle for upgraded components in a child zone This fixes #6385 by running `$rootScope.$digest` in a child zone. This prevents tasks started in the digest cycle from triggering the digest again, thus preventing the infinite loop. --- packages/upgrade/src/static/upgrade_module.ts | 18 ++++- .../integration/change_detection_spec.ts | 67 +++++++++---------- 2 files changed, 49 insertions(+), 36 deletions(-) diff --git a/packages/upgrade/src/static/upgrade_module.ts b/packages/upgrade/src/static/upgrade_module.ts index 5c32a814c9a944..0857a869f2d67f 100644 --- a/packages/upgrade/src/static/upgrade_module.ts +++ b/packages/upgrade/src/static/upgrade_module.ts @@ -207,8 +207,22 @@ export class UpgradeModule { // stabilizing setTimeout(() => { const $rootScope = $injector.get('$rootScope'); - const subscription = - this.ngZone.onMicrotaskEmpty.subscribe(() => $rootScope.$digest()); + const subscription = this.ngZone.onMicrotaskEmpty.subscribe(() => { + if (!Zone.current.get('isNgUpgradeZone')) { + Zone.current + .fork({ + name: 'ngUpgrade', + properties: {'isNgUpgradeZone': true}, + onInvokeTask: function( + delegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, + task: Task, applyThis: any, applyArgs: any) { + $rootScope.$digest(); + delegate.invokeTask(targetZone, task, applyThis, applyArgs); + }, + }) + .run(() => {$rootScope.$digest()}) + } + }); $rootScope.$on('$destroy', () => { subscription.unsubscribe(); }); }, 0); } diff --git a/packages/upgrade/test/static/integration/change_detection_spec.ts b/packages/upgrade/test/static/integration/change_detection_spec.ts index 96eae146a4dbde..4058341829a5ad 100644 --- a/packages/upgrade/test/static/integration/change_detection_spec.ts +++ b/packages/upgrade/test/static/integration/change_detection_spec.ts @@ -100,10 +100,8 @@ export function main() { ngOnChanges(changes: SimpleChanges) { if (changes['value'].isFirstChange()) return; - this.zone.onMicrotaskEmpty.subscribe( - () => { expect(element.textContent).toEqual('5'); }); - - Promise.resolve().then(() => this.valueFromPromise = changes['value'].currentValue); + this.zone.onStable.next(() => { expect(element.textContent).toEqual('5'); }); + Promise.resolve().then(() => {this.valueFromPromise = changes['value'].currentValue}); } } @@ -127,35 +125,36 @@ export function main() { }); })); - // This test demonstrates https://github.com/angular/angular/issues/6385 - // which was invalidly fixed by https://github.com/angular/angular/pull/6386 - // it('should not trigger $digest from an async operation in a watcher', async(() => { - // @Component({selector: 'my-app', template: ''}) - // class AppComponent { - // } - - // @NgModule({declarations: [AppComponent], imports: [BrowserModule]}) - // class Ng2Module { - // } - - // const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - // const ng1Module = angular.module('ng1', []).directive( - // 'myApp', adapter.downgradeNg2Component(AppComponent)); - - // const element = html(''); - - // adapter.bootstrap(element, ['ng1']).ready((ref) => { - // let doTimeout = false; - // let timeoutId: number; - // ref.ng1RootScope.$watch(() => { - // if (doTimeout && !timeoutId) { - // timeoutId = window.setTimeout(function() { - // timeoutId = null; - // }, 10); - // } - // }); - // doTimeout = true; - // }); - // })); + it('should not trigger $digest from an async operation in a watcher', async(() => { + @Component({selector: 'my-app', template: ''}) + class AppComponent { + } + + @NgModule({ + declarations: [AppComponent], + entryComponents: [AppComponent], + imports: [BrowserModule, UpgradeModule] + }) + class Ng2Module { + ngDoBootstrap() {} + } + + const ng1Module = angular.module('ng1', []).directive( + 'myApp', downgradeComponent({component: AppComponent})); + + const element = html(''); + + bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => { + let doTimeout = false; + let timeoutId: number; + let rootScope = upgrade.$injector.get('$rootScope'); + rootScope.$watch(() => { + if (doTimeout && !timeoutId) { + timeoutId = window.setTimeout(function() { timeoutId = null; }, 10); + } + }); + doTimeout = true; + }); + })); }); }