fix(upgrade): ensure downgraded components are created in the Angular zone (#18209)

PR Close #18209
This commit is contained in:
Georgios Kalpakas
2017-07-18 20:16:04 +03:00
committed by Miško Hevery
parent 6d7799fce9
commit 43c33d5663
8 changed files with 409 additions and 179 deletions

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ComponentFactory, ComponentFactoryResolver, Injector, Type} from '@angular/core';
import {ComponentFactory, ComponentFactoryResolver, Injector, NgZone, Type} from '@angular/core';
import * as angular from './angular1';
import {$COMPILE, $INJECTOR, $PARSE, INJECTOR_KEY, LAZY_MODULE_REF, REQUIRE_INJECTOR, REQUIRE_NG_MODEL} from './constants';
@ -72,6 +72,14 @@ export function downgradeComponent(info: {
$compile: angular.ICompileService,
$injector: angular.IInjectorService,
$parse: angular.IParseService): angular.IDirective {
// When using `UpgradeModule`, we don't need to ensure callbacks to Angular APIs (e.g. change
// detection) are run inside the Angular zone, because `$digest()` will be run inside the zone
// (except if explicitly escaped, in which case we shouldn't force it back in).
// When using `downgradeModule()` though, we need to ensure such callbacks are run inside the
// Angular zone.
let needsNgZone = false;
let wrapCallback = <T>(cb: () => T) => cb;
let ngZone: NgZone;
return {
restrict: 'E',
@ -89,10 +97,11 @@ export function downgradeComponent(info: {
if (!parentInjector) {
const lazyModuleRef = $injector.get(LAZY_MODULE_REF) as LazyModuleRef;
parentInjector = lazyModuleRef.injector || lazyModuleRef.promise;
needsNgZone = lazyModuleRef.needsNgZone;
parentInjector = lazyModuleRef.injector || lazyModuleRef.promise as Promise<Injector>;
}
const downgradeFn = (injector: Injector) => {
const doDowngrade = (injector: Injector) => {
const componentFactoryResolver: ComponentFactoryResolver =
injector.get(ComponentFactoryResolver);
const componentFactory: ComponentFactory<any> =
@ -106,13 +115,13 @@ export function downgradeComponent(info: {
const injectorPromise = new ParentInjectorPromise(element);
const facade = new DowngradeComponentAdapter(
id, element, attrs, scope, ngModel, injector, $injector, $compile, $parse,
componentFactory);
componentFactory, wrapCallback);
const projectableNodes = facade.compileContents();
facade.createComponent(projectableNodes);
facade.setupInputs(info.propagateDigest);
facade.setupInputs(needsNgZone, info.propagateDigest);
facade.setupOutputs();
facade.registerCleanup();
facade.registerCleanup(needsNgZone);
injectorPromise.resolve(facade.getInjector());
@ -123,6 +132,16 @@ export function downgradeComponent(info: {
}
};
const downgradeFn = !needsNgZone ? doDowngrade : (injector: Injector) => {
if (!ngZone) {
ngZone = injector.get(NgZone);
wrapCallback = <T>(cb: () => T) => () =>
NgZone.isInAngularZone() ? cb() : ngZone.run(cb);
}
wrapCallback(() => doDowngrade(injector))();
};
if (isThenable<Injector>(parentInjector)) {
parentInjector.then(downgradeFn);
} else {