fix(upgrade): call setInterval outside the Angular zone

This wraps the $interval service when using upgrade to run the
$interval() call outside the Angular zone. However, the callback is
invoked within the Angular zone, so changes still propagate to
downgraded components.
This commit is contained in:
Michael Giambalvo
2017-05-16 18:03:25 -07:00
committed by Alex Rickabaugh
parent bb2fc6b8da
commit 269bbe0e7d
4 changed files with 72 additions and 1 deletions

View File

@ -158,6 +158,12 @@ export interface IInjectorService {
has(key: string): boolean;
}
export interface IIntervalService {
(func: Function, delay: number, count?: number, invokeApply?: boolean,
...args: any[]): Promise<any>;
cancel(promise: Promise<any>): boolean;
}
export interface ITestabilityService {
findBindings(element: Element, expression: string, opt_exactMatch?: boolean): Element[];
findModels(element: Element, expression: string, opt_exactMatch?: boolean): Element[];

View File

@ -11,6 +11,7 @@ export const $CONTROLLER = '$controller';
export const $DELEGATE = '$delegate';
export const $HTTP_BACKEND = '$httpBackend';
export const $INJECTOR = '$injector';
export const $INTERVAL = '$interval';
export const $PARSE = '$parse';
export const $PROVIDE = '$provide';
export const $ROOT_SCOPE = '$rootScope';

View File

@ -9,7 +9,7 @@
import {Injector, NgModule, NgZone, Testability, ɵNOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR as NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR} from '@angular/core';
import * as angular from '../common/angular1';
import {$$TESTABILITY, $DELEGATE, $INJECTOR, $PROVIDE, INJECTOR_KEY, UPGRADE_MODULE_NAME} from '../common/constants';
import {$$TESTABILITY, $DELEGATE, $INJECTOR, $INTERVAL, $PROVIDE, INJECTOR_KEY, UPGRADE_MODULE_NAME} from '../common/constants';
import {controllerKey} from '../common/util';
import {angular1Providers, setTempInjectorRef} from './angular1_providers';
@ -190,6 +190,33 @@ export class UpgradeModule {
}
]);
}
if ($injector.has($INTERVAL)) {
$provide.decorator($INTERVAL, [
$DELEGATE,
(intervalDelegate: angular.IIntervalService) => {
// Wrap the $interval service so that setInterval is called outside NgZone,
// but the callback is still invoked within it. This is so that $interval
// won't block stability, which preserves the behavior from AngularJS.
let wrappedInterval =
(fn: Function, delay: number, count?: number, invokeApply?: boolean,
...pass: any[]) => {
return this.ngZone.runOutsideAngular(() => {
return intervalDelegate((...args: any[]) => {
// Run callback in the next VM turn - $interval calls
// $rootScope.$apply, and running the callback in NgZone will
// cause a '$digest already in progress' error if it's in the
// same vm turn.
setTimeout(() => { this.ngZone.run(() => fn(...args)); });
}, delay, count, invokeApply, ...pass);
});
};
(wrappedInterval as any)['cancel'] = intervalDelegate.cancel;
return wrappedInterval;
}
]);
}
}
])