fix(ngUpgrade): to work with @NgModule
We changed the bootstrap order: 1. create NgZone 2. bootstrap ng1 inside NgZone and upgrade ng1 components to ng2 components. 3. bootstrap ng2 with NgZone Note: Previous footgun behavior was: bootstrap ng2 first to extract NgZone, so that ng1 bootstrap can happen in NgZone. This meant that if ng2 bootstrap eagerly compiled a component which contained ng1 components, then we did not have complete metadata.
This commit is contained in:
parent
37f138e83d
commit
d21331e902
|
@ -339,11 +339,16 @@ export class PlatformRef_ extends PlatformRef {
|
||||||
dispose(): void { this.destroy(); }
|
dispose(): void { this.destroy(); }
|
||||||
|
|
||||||
bootstrapModuleFactory<M>(moduleFactory: NgModuleFactory<M>): Promise<NgModuleRef<M>> {
|
bootstrapModuleFactory<M>(moduleFactory: NgModuleFactory<M>): Promise<NgModuleRef<M>> {
|
||||||
|
return this._bootstrapModuleFactoryWithZone(moduleFactory, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _bootstrapModuleFactoryWithZone<M>(moduleFactory: NgModuleFactory<M>, ngZone: NgZone):
|
||||||
|
Promise<NgModuleRef<M>> {
|
||||||
// Note: We need to create the NgZone _before_ we instantiate the module,
|
// Note: We need to create the NgZone _before_ we instantiate the module,
|
||||||
// as instantiating the module creates some providers eagerly.
|
// as instantiating the module creates some providers eagerly.
|
||||||
// So we create a mini parent injector that just contains the new NgZone and
|
// So we create a mini parent injector that just contains the new NgZone and
|
||||||
// pass that as parent to the NgModuleFactory.
|
// pass that as parent to the NgModuleFactory.
|
||||||
const ngZone = new NgZone({enableLongStackTrace: isDevMode()});
|
if (!ngZone) ngZone = new NgZone({enableLongStackTrace: isDevMode()});
|
||||||
// Attention: Don't use ApplicationRef.run here,
|
// Attention: Don't use ApplicationRef.run here,
|
||||||
// as we want to be sure that all possible constructor calls are inside `ngZone.run`!
|
// as we want to be sure that all possible constructor calls are inside `ngZone.run`!
|
||||||
return ngZone.run(() => {
|
return ngZone.run(() => {
|
||||||
|
@ -371,11 +376,17 @@ export class PlatformRef_ extends PlatformRef {
|
||||||
bootstrapModule<M>(
|
bootstrapModule<M>(
|
||||||
moduleType: ConcreteType<M>,
|
moduleType: ConcreteType<M>,
|
||||||
compilerOptions: CompilerOptions|CompilerOptions[] = []): Promise<NgModuleRef<M>> {
|
compilerOptions: CompilerOptions|CompilerOptions[] = []): Promise<NgModuleRef<M>> {
|
||||||
|
return this._bootstrapModuleWithZone(moduleType, compilerOptions, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _bootstrapModuleWithZone<M>(
|
||||||
|
moduleType: ConcreteType<M>, compilerOptions: CompilerOptions|CompilerOptions[] = [],
|
||||||
|
ngZone: NgZone): Promise<NgModuleRef<M>> {
|
||||||
const compilerFactory: CompilerFactory = this.injector.get(CompilerFactory);
|
const compilerFactory: CompilerFactory = this.injector.get(CompilerFactory);
|
||||||
const compiler = compilerFactory.createCompiler(
|
const compiler = compilerFactory.createCompiler(
|
||||||
compilerOptions instanceof Array ? compilerOptions : [compilerOptions]);
|
compilerOptions instanceof Array ? compilerOptions : [compilerOptions]);
|
||||||
return compiler.compileModuleAsync(moduleType)
|
return compiler.compileModuleAsync(moduleType)
|
||||||
.then((moduleFactory) => this.bootstrapModuleFactory(moduleFactory));
|
.then((moduleFactory) => this._bootstrapModuleFactoryWithZone(moduleFactory, ngZone));
|
||||||
}
|
}
|
||||||
|
|
||||||
private _moduleDoBootstrap(moduleRef: NgModuleInjector<any>) {
|
private _moduleDoBootstrap(moduleRef: NgModuleInjector<any>) {
|
||||||
|
|
|
@ -115,7 +115,10 @@ export interface IControllerService {
|
||||||
(controllerName: string, locals?: any): any;
|
(controllerName: string, locals?: any): any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IInjectorService { get(key: string): any; }
|
export interface IInjectorService {
|
||||||
|
get(key: string): any;
|
||||||
|
has(key: string): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ITestabilityService {
|
export interface ITestabilityService {
|
||||||
findBindings(element: Element, expression: string, opt_exactMatch?: boolean): Element[];
|
findBindings(element: Element, expression: string, opt_exactMatch?: boolean): Element[];
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Compiler, CompilerFactory, ComponentFactory, ComponentResolver, Injector, NgModule, NgModuleRef, NgZone, PlatformRef, Provider, ReflectiveInjector, Testability, Type, provide} from '@angular/core';
|
import {BaseException, Compiler, ComponentFactory, Injector, NgModule, NgModuleRef, NgZone, Provider, Testability, Type} from '@angular/core';
|
||||||
import {BrowserModule} from '@angular/platform-browser';
|
import {BrowserModule} from '@angular/platform-browser';
|
||||||
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
||||||
|
|
||||||
|
@ -59,11 +59,11 @@ var upgradeCount: number = 0;
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
* var adapter = new UpgradeAdapter();
|
* var adapter = new UpgradeAdapter(forwardRef(() => MyNg2Module));
|
||||||
* var module = angular.module('myExample', []);
|
* var module = angular.module('myExample', []);
|
||||||
* module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
* module.directive('ng2Comp', adapter.downgradeNg2Component(Ng2));
|
||||||
*
|
*
|
||||||
* module.directive('ng1', function() {
|
* module.directive('ng1Hello', function() {
|
||||||
* return {
|
* return {
|
||||||
* scope: { title: '=' },
|
* scope: { title: '=' },
|
||||||
* template: 'ng1[Hello {{title}}!](<span ng-transclude></span>)'
|
* template: 'ng1[Hello {{title}}!](<span ng-transclude></span>)'
|
||||||
|
@ -72,20 +72,28 @@ var upgradeCount: number = 0;
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @Component({
|
* @Component({
|
||||||
* selector: 'ng2',
|
* selector: 'ng2-comp',
|
||||||
* inputs: ['name'],
|
* inputs: ['name'],
|
||||||
* template: 'ng2[<ng1 [title]="name">transclude</ng1>](<ng-content></ng-content>)',
|
* template: 'ng2[<ng1-hello [title]="name">transclude</ng1-hello>](<ng-content></ng-content>)',
|
||||||
* directives: [adapter.upgradeNg1Component('ng1')]
|
* directives:
|
||||||
* })
|
* })
|
||||||
* class Ng2 {
|
* class Ng2Component {
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* document.body.innerHTML = '<ng2 name="World">project</ng2>';
|
* @NgModule({
|
||||||
|
* declarations: [Ng2Component, adapter.upgradeNg1Component('ng1Hello')],
|
||||||
|
* imports: [BrowserModule]
|
||||||
|
* })
|
||||||
|
* class MyNg2Module {}
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* document.body.innerHTML = '<ng2-comp name="World">project</ng2-comp>';
|
||||||
*
|
*
|
||||||
* adapter.bootstrap(document.body, ['myExample']).ready(function() {
|
* adapter.bootstrap(document.body, ['myExample']).ready(function() {
|
||||||
* expect(document.body.textContent).toEqual(
|
* expect(document.body.textContent).toEqual(
|
||||||
* "ng2[ng1[Hello World!](transclude)](project)");
|
* "ng2[ng1[Hello World!](transclude)](project)");
|
||||||
* });
|
* });
|
||||||
|
*
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @experimental
|
* @experimental
|
||||||
|
@ -95,14 +103,26 @@ export class UpgradeAdapter {
|
||||||
private idPrefix: string = `NG2_UPGRADE_${upgradeCount++}_`;
|
private idPrefix: string = `NG2_UPGRADE_${upgradeCount++}_`;
|
||||||
/* @internal */
|
/* @internal */
|
||||||
private upgradedComponents: Type[] = [];
|
private upgradedComponents: Type[] = [];
|
||||||
/* @internal */
|
/**
|
||||||
private downgradedComponents: {[name: string]: UpgradeNg1ComponentAdapterBuilder} = {};
|
* An internal map of ng1 components which need to up upgraded to ng2.
|
||||||
|
*
|
||||||
|
* We can't upgrade until injector is instantiated and we can retrieve the component metadata.
|
||||||
|
* For this reason we keep a list of components to upgrade until ng1 injector is bootstrapped.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
private ng1ComponentsToBeUpgraded: {[name: string]: UpgradeNg1ComponentAdapterBuilder} = {};
|
||||||
/* @internal */
|
/* @internal */
|
||||||
private providers: Array<Type|Provider|any[]|any> = [];
|
private providers: Array<Type|Provider|any[]|any> = [];
|
||||||
|
|
||||||
// the ng2AppModule param should be required once the deprecated @Component.directives prop is
|
// the ng2AppModule param should be required once the deprecated @Component.directives prop is
|
||||||
// removed
|
// removed
|
||||||
constructor(private ng2AppModule?: Type) {}
|
constructor(private ng2AppModule?: Type) {
|
||||||
|
if (arguments.length && !ng2AppModule) {
|
||||||
|
throw new BaseException(
|
||||||
|
'UpgradeAdapter constructor called with undefined instead of a ng module type');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows Angular v2 Component to be used from AngularJS v1.
|
* Allows Angular v2 Component to be used from AngularJS v1.
|
||||||
|
@ -132,7 +152,7 @@ export class UpgradeAdapter {
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
* var adapter = new UpgradeAdapter();
|
* var adapter = new UpgradeAdapter(forwardRef(() => MyNg2Module));
|
||||||
* var module = angular.module('myExample', []);
|
* var module = angular.module('myExample', []);
|
||||||
* module.directive('greet', adapter.downgradeNg2Component(Greeter));
|
* module.directive('greet', adapter.downgradeNg2Component(Greeter));
|
||||||
*
|
*
|
||||||
|
@ -145,6 +165,12 @@ export class UpgradeAdapter {
|
||||||
* @Input() name: string;
|
* @Input() name: string;
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
|
* @NgModule({
|
||||||
|
* declarations: [Greeter],
|
||||||
|
* imports: [BrowserModule]
|
||||||
|
* })
|
||||||
|
* class MyNg2Module {}
|
||||||
|
*
|
||||||
* document.body.innerHTML =
|
* document.body.innerHTML =
|
||||||
* 'ng1 template: <greet salutation="Hello" [name]="world">text</greet>';
|
* 'ng1 template: <greet salutation="Hello" [name]="world">text</greet>';
|
||||||
*
|
*
|
||||||
|
@ -204,7 +230,7 @@ export class UpgradeAdapter {
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
* var adapter = new UpgradeAdapter();
|
* var adapter = new UpgradeAdapter(forwardRef(() => MyNg2Module));
|
||||||
* var module = angular.module('myExample', []);
|
* var module = angular.module('myExample', []);
|
||||||
*
|
*
|
||||||
* module.directive('greet', function() {
|
* module.directive('greet', function() {
|
||||||
|
@ -219,11 +245,16 @@ export class UpgradeAdapter {
|
||||||
* @Component({
|
* @Component({
|
||||||
* selector: 'ng2',
|
* selector: 'ng2',
|
||||||
* template: 'ng2 template: <greet salutation="Hello" [name]="world">text</greet>'
|
* template: 'ng2 template: <greet salutation="Hello" [name]="world">text</greet>'
|
||||||
* directives: [adapter.upgradeNg1Component('greet')]
|
|
||||||
* })
|
* })
|
||||||
* class Ng2 {
|
* class Ng2 {
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
|
* @NgModule({
|
||||||
|
* declarations: [Ng2, adapter.upgradeNg1Component('greet')],
|
||||||
|
* imports: [BrowserModule]
|
||||||
|
* })
|
||||||
|
* class MyNg2Module {}
|
||||||
|
*
|
||||||
* document.body.innerHTML = '<ng2></ng2>';
|
* document.body.innerHTML = '<ng2></ng2>';
|
||||||
*
|
*
|
||||||
* adapter.bootstrap(document.body, ['myExample']).ready(function() {
|
* adapter.bootstrap(document.body, ['myExample']).ready(function() {
|
||||||
|
@ -232,10 +263,11 @@ export class UpgradeAdapter {
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
upgradeNg1Component(name: string): Type {
|
upgradeNg1Component(name: string): Type {
|
||||||
if ((<any>this.downgradedComponents).hasOwnProperty(name)) {
|
if ((<any>this.ng1ComponentsToBeUpgraded).hasOwnProperty(name)) {
|
||||||
return this.downgradedComponents[name].type;
|
return this.ng1ComponentsToBeUpgraded[name].type;
|
||||||
} else {
|
} else {
|
||||||
return (this.downgradedComponents[name] = new UpgradeNg1ComponentAdapterBuilder(name)).type;
|
return (this.ng1ComponentsToBeUpgraded[name] = new UpgradeNg1ComponentAdapterBuilder(name))
|
||||||
|
.type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,12 +296,17 @@ export class UpgradeAdapter {
|
||||||
* @Component({
|
* @Component({
|
||||||
* selector: 'ng2',
|
* selector: 'ng2',
|
||||||
* inputs: ['name'],
|
* inputs: ['name'],
|
||||||
* template: 'ng2[<ng1 [title]="name">transclude</ng1>](<ng-content></ng-content>)',
|
* template: 'ng2[<ng1 [title]="name">transclude</ng1>](<ng-content></ng-content>)'
|
||||||
* directives: [adapter.upgradeNg1Component('ng1')]
|
|
||||||
* })
|
* })
|
||||||
* class Ng2 {
|
* class Ng2 {
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
|
* @NgModule({
|
||||||
|
* declarations: [Ng2, adapter.upgradeNg1Component('ng1')],
|
||||||
|
* imports: [BrowserModule]
|
||||||
|
* })
|
||||||
|
* class MyNg2Module {}
|
||||||
|
*
|
||||||
* document.body.innerHTML = '<ng2 name="World">project</ng2>';
|
* document.body.innerHTML = '<ng2 name="World">project</ng2>';
|
||||||
*
|
*
|
||||||
* adapter.bootstrap(document.body, ['myExample']).ready(function() {
|
* adapter.bootstrap(document.body, ['myExample']).ready(function() {
|
||||||
|
@ -280,55 +317,35 @@ export class UpgradeAdapter {
|
||||||
*/
|
*/
|
||||||
bootstrap(element: Element, modules?: any[], config?: angular.IAngularBootstrapConfig):
|
bootstrap(element: Element, modules?: any[], config?: angular.IAngularBootstrapConfig):
|
||||||
UpgradeAdapterRef {
|
UpgradeAdapterRef {
|
||||||
|
const ngZone =
|
||||||
|
new NgZone({enableLongStackTrace: Zone.hasOwnProperty('longStackTraceZoneSpec')});
|
||||||
var upgrade = new UpgradeAdapterRef();
|
var upgrade = new UpgradeAdapterRef();
|
||||||
var ng1Injector: angular.IInjectorService = null;
|
var ng1Injector: angular.IInjectorService = null;
|
||||||
var platformRef: PlatformRef = platformBrowserDynamic();
|
var moduleRef: NgModuleRef<any> = null;
|
||||||
var providers = [
|
|
||||||
{provide: NG1_INJECTOR, useFactory: () => ng1Injector},
|
|
||||||
{provide: NG1_COMPILE, useFactory: () => ng1Injector.get(NG1_COMPILE)}, this.providers
|
|
||||||
];
|
|
||||||
|
|
||||||
@NgModule({providers: providers, imports: [BrowserModule]})
|
|
||||||
class DynamicModule {
|
|
||||||
ngDoBootstrap() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
platformRef.bootstrapModule(<any>this.ng2AppModule || DynamicModule).then((moduleRef) => {
|
|
||||||
ng1Injector = this._afterNg2ModuleBootstrap(moduleRef, upgrade, element, modules, config);
|
|
||||||
});
|
|
||||||
return upgrade;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _afterNg2ModuleBootstrap(
|
|
||||||
moduleRef: NgModuleRef<any>, upgrade: UpgradeAdapterRef, element: Element, modules?: any[],
|
|
||||||
config?: angular.IAngularBootstrapConfig): angular.IInjectorService {
|
|
||||||
const boundCompiler: Compiler = moduleRef.injector.get(Compiler);
|
|
||||||
var ng1Injector: angular.IInjectorService = null;
|
|
||||||
var injector: Injector = moduleRef.injector;
|
|
||||||
var ngZone: NgZone = injector.get(NgZone);
|
|
||||||
var delayApplyExps: Function[] = [];
|
var delayApplyExps: Function[] = [];
|
||||||
var original$applyFn: Function;
|
var original$applyFn: Function;
|
||||||
var rootScopePrototype: any;
|
var rootScopePrototype: any;
|
||||||
var rootScope: angular.IRootScopeService;
|
var rootScope: angular.IRootScopeService;
|
||||||
var componentFactoryRefMap: ComponentFactoryRefMap = {};
|
var componentFactoryRefMap: ComponentFactoryRefMap = {};
|
||||||
var ng1Module = angular.module(this.idPrefix, modules);
|
var ng1Module = angular.module(this.idPrefix, modules);
|
||||||
var ng1BootstrapPromise: Promise<any> = null;
|
var ng1BootstrapPromise: Promise<any>;
|
||||||
var ng1compilePromise: Promise<any> = null;
|
var ng1compilePromise: Promise<any>;
|
||||||
ng1Module.value(NG2_INJECTOR, injector)
|
ng1Module.factory(NG2_INJECTOR, () => moduleRef.injector.get(Injector))
|
||||||
.value(NG2_ZONE, ngZone)
|
.value(NG2_ZONE, ngZone)
|
||||||
.value(NG2_COMPILER, boundCompiler)
|
.factory(NG2_COMPILER, () => moduleRef.injector.get(Compiler))
|
||||||
.value(NG2_COMPONENT_FACTORY_REF_MAP, componentFactoryRefMap)
|
.value(NG2_COMPONENT_FACTORY_REF_MAP, componentFactoryRefMap)
|
||||||
.config([
|
.config([
|
||||||
'$provide', '$injector',
|
'$provide', '$injector',
|
||||||
(provide: any /** TODO #???? */, ng1Injector: any /** TODO #???? */) => {
|
(provide: any /** TODO #???? */, ng1Injector: angular.IInjectorService) => {
|
||||||
provide.decorator(NG1_ROOT_SCOPE, [
|
provide.decorator(NG1_ROOT_SCOPE, [
|
||||||
'$delegate',
|
'$delegate',
|
||||||
function(rootScopeDelegate: angular.IRootScopeService) {
|
function(rootScopeDelegate: angular.IRootScopeService) {
|
||||||
|
// Capture the root apply so that we can delay first call to $apply until we
|
||||||
|
// bootstrap Angular 2 and then we replay and restore the $apply.
|
||||||
rootScopePrototype = rootScopeDelegate.constructor.prototype;
|
rootScopePrototype = rootScopeDelegate.constructor.prototype;
|
||||||
if (rootScopePrototype.hasOwnProperty('$apply')) {
|
if (rootScopePrototype.hasOwnProperty('$apply')) {
|
||||||
original$applyFn = rootScopePrototype.$apply;
|
original$applyFn = rootScopePrototype.$apply;
|
||||||
rootScopePrototype.$apply = (exp: any /** TODO #???? */) =>
|
rootScopePrototype.$apply = (exp: any) => delayApplyExps.push(exp);
|
||||||
delayApplyExps.push(exp);
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Failed to find \'$apply\' on \'$rootScope\'!');
|
throw new Error('Failed to find \'$apply\' on \'$rootScope\'!');
|
||||||
}
|
}
|
||||||
|
@ -339,12 +356,12 @@ export class UpgradeAdapter {
|
||||||
provide.decorator(NG1_TESTABILITY, [
|
provide.decorator(NG1_TESTABILITY, [
|
||||||
'$delegate',
|
'$delegate',
|
||||||
function(testabilityDelegate: angular.ITestabilityService) {
|
function(testabilityDelegate: angular.ITestabilityService) {
|
||||||
var ng2Testability: Testability = injector.get(Testability);
|
|
||||||
|
|
||||||
var origonalWhenStable: Function = testabilityDelegate.whenStable;
|
var originalWhenStable: Function = testabilityDelegate.whenStable;
|
||||||
var newWhenStable = (callback: Function): void => {
|
var newWhenStable = (callback: Function): void => {
|
||||||
var whenStableContext: any = this;
|
var whenStableContext: any = this;
|
||||||
origonalWhenStable.call(this, function() {
|
originalWhenStable.call(this, function() {
|
||||||
|
var ng2Testability: Testability = moduleRef.injector.get(Testability);
|
||||||
if (ng2Testability.isStable()) {
|
if (ng2Testability.isStable()) {
|
||||||
callback.apply(this, arguments);
|
callback.apply(this, arguments);
|
||||||
} else {
|
} else {
|
||||||
|
@ -366,12 +383,31 @@ export class UpgradeAdapter {
|
||||||
'$injector', '$rootScope',
|
'$injector', '$rootScope',
|
||||||
(injector: angular.IInjectorService, rootScope: angular.IRootScopeService) => {
|
(injector: angular.IInjectorService, rootScope: angular.IRootScopeService) => {
|
||||||
ng1Injector = injector;
|
ng1Injector = injector;
|
||||||
ngZone.onMicrotaskEmpty.subscribe({
|
UpgradeNg1ComponentAdapterBuilder.resolve(this.ng1ComponentsToBeUpgraded, injector)
|
||||||
next: (_: any /** TODO #???? */) =>
|
.then(() => {
|
||||||
ngZone.runOutsideAngular(() => rootScope.$evalAsync())
|
// At this point we have ng1 injector and we have lifted ng1 components into ng2, we
|
||||||
});
|
// now can bootstrap ng2.
|
||||||
UpgradeNg1ComponentAdapterBuilder.resolve(this.downgradedComponents, injector)
|
var DynamicNgUpgradeModule =
|
||||||
.then(resolve, reject);
|
NgModule({
|
||||||
|
providers: [
|
||||||
|
{provide: NG1_INJECTOR, useFactory: () => ng1Injector},
|
||||||
|
{provide: NG1_COMPILE, useFactory: () => ng1Injector.get(NG1_COMPILE)},
|
||||||
|
this.providers
|
||||||
|
],
|
||||||
|
imports: this.ng2AppModule ? [this.ng2AppModule] : [BrowserModule]
|
||||||
|
}).Class({constructor: function() {}, ngDoBootstrap: function() {}});
|
||||||
|
(platformBrowserDynamic() as any)
|
||||||
|
._bootstrapModuleWithZone(DynamicNgUpgradeModule, undefined, ngZone)
|
||||||
|
.then((ref: NgModuleRef<any>) => {
|
||||||
|
moduleRef = ref;
|
||||||
|
angular.element(element).data(
|
||||||
|
controllerKey(NG2_INJECTOR), moduleRef.injector);
|
||||||
|
ngZone.onMicrotaskEmpty.subscribe({
|
||||||
|
next: (_: any) => ngZone.runOutsideAngular(() => rootScope.$evalAsync())
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(resolve, reject);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
@ -380,7 +416,6 @@ export class UpgradeAdapter {
|
||||||
var windowAngular = (window as any /** TODO #???? */)['angular'];
|
var windowAngular = (window as any /** TODO #???? */)['angular'];
|
||||||
windowAngular.resumeBootstrap = undefined;
|
windowAngular.resumeBootstrap = undefined;
|
||||||
|
|
||||||
angular.element(element).data(controllerKey(NG2_INJECTOR), injector);
|
|
||||||
ngZone.run(() => { angular.bootstrap(element, [this.idPrefix], config); });
|
ngZone.run(() => { angular.bootstrap(element, [this.idPrefix], config); });
|
||||||
ng1BootstrapPromise = new Promise((resolve, reject) => {
|
ng1BootstrapPromise = new Promise((resolve, reject) => {
|
||||||
if (windowAngular.resumeBootstrap) {
|
if (windowAngular.resumeBootstrap) {
|
||||||
|
@ -396,9 +431,12 @@ export class UpgradeAdapter {
|
||||||
});
|
});
|
||||||
|
|
||||||
Promise.all([ng1BootstrapPromise, ng1compilePromise])
|
Promise.all([ng1BootstrapPromise, ng1compilePromise])
|
||||||
.then(() => { return this.compileNg2Components(boundCompiler, componentFactoryRefMap); })
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
ngZone.run(() => {
|
return this.compileNg2Components(
|
||||||
|
moduleRef.injector.get(Compiler), componentFactoryRefMap);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
moduleRef.injector.get(NgZone).run(() => {
|
||||||
if (rootScopePrototype) {
|
if (rootScopePrototype) {
|
||||||
rootScopePrototype.$apply = original$applyFn; // restore original $apply
|
rootScopePrototype.$apply = original$applyFn; // restore original $apply
|
||||||
while (delayApplyExps.length) {
|
while (delayApplyExps.length) {
|
||||||
|
@ -409,7 +447,7 @@ export class UpgradeAdapter {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, onError);
|
}, onError);
|
||||||
return ng1Injector;
|
return upgrade;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -528,7 +566,7 @@ export class UpgradeAdapter {
|
||||||
var promises: Array<Promise<ComponentFactory<any>>> = [];
|
var promises: Array<Promise<ComponentFactory<any>>> = [];
|
||||||
var types = this.upgradedComponents;
|
var types = this.upgradedComponents;
|
||||||
for (var i = 0; i < types.length; i++) {
|
for (var i = 0; i < types.length; i++) {
|
||||||
promises.push(compiler.compileComponentAsync(<any>types[i]));
|
promises.push(compiler.compileComponentAsync(<any>types[i], this.ng2AppModule));
|
||||||
}
|
}
|
||||||
return Promise.all(promises).then((componentFactories: Array<ComponentFactory<any>>) => {
|
return Promise.all(promises).then((componentFactories: Array<ComponentFactory<any>>) => {
|
||||||
var types = this.upgradedComponents;
|
var types = this.upgradedComponents;
|
||||||
|
|
|
@ -127,7 +127,7 @@ export class UpgradeNg1ComponentAdapterBuilder {
|
||||||
|
|
||||||
compileTemplate(
|
compileTemplate(
|
||||||
compile: angular.ICompileService, templateCache: angular.ITemplateCacheService,
|
compile: angular.ICompileService, templateCache: angular.ITemplateCacheService,
|
||||||
httpBackend: angular.IHttpBackendService): Promise<any> {
|
httpBackend: angular.IHttpBackendService): Promise<angular.ILinkFn> {
|
||||||
if (this.directive.template !== undefined) {
|
if (this.directive.template !== undefined) {
|
||||||
this.linkFn = compileHtml(
|
this.linkFn = compileHtml(
|
||||||
typeof this.directive.template === 'function' ? this.directive.template() :
|
typeof this.directive.template === 'function' ? this.directive.template() :
|
||||||
|
@ -162,10 +162,13 @@ export class UpgradeNg1ComponentAdapterBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upgrade ng1 components into Angular 2.
|
||||||
|
*/
|
||||||
static resolve(
|
static resolve(
|
||||||
exportedComponents: {[name: string]: UpgradeNg1ComponentAdapterBuilder},
|
exportedComponents: {[name: string]: UpgradeNg1ComponentAdapterBuilder},
|
||||||
injector: angular.IInjectorService): Promise<any> {
|
injector: angular.IInjectorService): Promise<angular.ILinkFn[]> {
|
||||||
var promises: any[] /** TODO #9100 */ = [];
|
var promises: Promise<angular.ILinkFn>[] = [];
|
||||||
var compile: angular.ICompileService = injector.get(NG1_COMPILE);
|
var compile: angular.ICompileService = injector.get(NG1_COMPILE);
|
||||||
var templateCache: angular.ITemplateCacheService = injector.get(NG1_TEMPLATE_CACHE);
|
var templateCache: angular.ITemplateCacheService = injector.get(NG1_TEMPLATE_CACHE);
|
||||||
var httpBackend: angular.IHttpBackendService = injector.get(NG1_HTTP_BACKEND);
|
var httpBackend: angular.IHttpBackendService = injector.get(NG1_HTTP_BACKEND);
|
||||||
|
@ -176,7 +179,8 @@ export class UpgradeNg1ComponentAdapterBuilder {
|
||||||
exportedComponent.directive = exportedComponent.extractDirective(injector);
|
exportedComponent.directive = exportedComponent.extractDirective(injector);
|
||||||
exportedComponent.$controller = $controller;
|
exportedComponent.$controller = $controller;
|
||||||
exportedComponent.extractBindings();
|
exportedComponent.extractBindings();
|
||||||
var promise = exportedComponent.compileTemplate(compile, templateCache, httpBackend);
|
var promise: Promise<angular.ILinkFn> =
|
||||||
|
exportedComponent.compileTemplate(compile, templateCache, httpBackend);
|
||||||
if (promise) promises.push(promise);
|
if (promise) promises.push(promise);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,10 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Class, Component, EventEmitter, Testability, disposePlatform, provide} from '@angular/core';
|
import {Class, Component, EventEmitter, Inject, NgModule, OpaqueToken, Testability, disposePlatform} from '@angular/core';
|
||||||
|
import {async} from '@angular/core/testing';
|
||||||
import {AsyncTestCompleter, ddescribe, describe, expect, iit, inject, it} from '@angular/core/testing/testing_internal';
|
import {AsyncTestCompleter, ddescribe, describe, expect, iit, inject, it} from '@angular/core/testing/testing_internal';
|
||||||
|
import {BrowserModule} from '@angular/platform-browser';
|
||||||
import {UpgradeAdapter} from '@angular/upgrade';
|
import {UpgradeAdapter} from '@angular/upgrade';
|
||||||
import * as angular from '@angular/upgrade/src/angular_js';
|
import * as angular from '@angular/upgrade/src/angular_js';
|
||||||
|
|
||||||
|
@ -60,7 +62,6 @@ export function main() {
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('scope/component change-detection', () => {
|
describe('scope/component change-detection', () => {
|
||||||
it('should interleave scope and component expressions',
|
it('should interleave scope and component expressions',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
|
@ -269,6 +270,41 @@ export function main() {
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
// this test should be removed ones the deprecated @Components.directives prop is removed
|
||||||
|
// we should rewrite all of the tests in this file to use modules instead.
|
||||||
|
it('should downgrade ng2 component that is part of a module', async(() => {
|
||||||
|
const ng1Module = angular.module('ng1', []);
|
||||||
|
|
||||||
|
ng1Module.component('ng1Component', {template: '<ng2-component></ng2-component>'});
|
||||||
|
|
||||||
|
const SpecialValue = new OpaqueToken('special test value');
|
||||||
|
|
||||||
|
const Ng2Component = Component({
|
||||||
|
selector: 'ng2-component',
|
||||||
|
template: '<span>test: {{value}}</span>'
|
||||||
|
}).Class({
|
||||||
|
constructor: [
|
||||||
|
Inject(SpecialValue), function Ng2Component(value: number) { this.value = value; }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
const Ng2AppModule =
|
||||||
|
NgModule({
|
||||||
|
declarations: [Ng2Component],
|
||||||
|
imports: [BrowserModule],
|
||||||
|
providers: [{provide: SpecialValue, useValue: 23}]
|
||||||
|
}).Class({constructor: function Ng2AppModule() {}, ngDoBootstrap: function() {}});
|
||||||
|
|
||||||
|
const adapter = new UpgradeAdapter(Ng2AppModule);
|
||||||
|
ng1Module.directive('ng2Component', adapter.downgradeNg2Component(Ng2Component));
|
||||||
|
var element = html('<ng1-component></ng1-component>');
|
||||||
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
||||||
|
expect(multiTrim(document.body.getElementsByTagName('ng2-component')[0].innerHTML))
|
||||||
|
.toEqual('<span>test: 23</span>');
|
||||||
|
ref.dispose();
|
||||||
|
});
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('upgrade ng1 component', () => {
|
describe('upgrade ng1 component', () => {
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Component, EventEmitter, Input, NgModule, Output} from '@angular/core';
|
import {Component, EventEmitter, Input, NgModule, Output, forwardRef} from '@angular/core';
|
||||||
|
import {BrowserModule} from '@angular/platform-browser';
|
||||||
import {UpgradeAdapter} from '@angular/upgrade';
|
import {UpgradeAdapter} from '@angular/upgrade';
|
||||||
|
|
||||||
declare var angular: any;
|
declare var angular: any;
|
||||||
|
@ -26,13 +27,12 @@ var styles = [`
|
||||||
}
|
}
|
||||||
`];
|
`];
|
||||||
|
|
||||||
var adapter: UpgradeAdapter = new UpgradeAdapter(Ng2AppModule);
|
var adapter = new UpgradeAdapter(forwardRef(() => Ng2AppModule));
|
||||||
|
|
||||||
var ng1module = angular.module('myExample', []);
|
var ng1module = angular.module('myExample', []);
|
||||||
|
|
||||||
ng1module.controller('Index', function($scope: any /** TODO #9100 */) { $scope.name = 'World'; });
|
ng1module.controller('Index', function($scope: any /** TODO #9100 */) { $scope.name = 'World'; });
|
||||||
|
|
||||||
ng1module.directive('user', function() {
|
ng1module.directive('ng1User', function() {
|
||||||
return {
|
return {
|
||||||
scope: {handle: '@', reset: '&'},
|
scope: {handle: '@', reset: '&'},
|
||||||
template: `
|
template: `
|
||||||
|
@ -62,13 +62,12 @@ class Pane {
|
||||||
<table cellpadding="3">
|
<table cellpadding="3">
|
||||||
<tr>
|
<tr>
|
||||||
<td><ng-content></ng-content></td>
|
<td><ng-content></ng-content></td>
|
||||||
<td><user [handle]="user" (reset)="reset.emit()"></user></td>
|
<td><ng1-user [handle]="user" (reset)="reset.emit()"></ng1-user></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</pane>
|
</pane>
|
||||||
</div>`,
|
</div>`,
|
||||||
styles: styles,
|
styles: styles
|
||||||
directives: [Pane, adapter.upgradeNg1Component('user')]
|
|
||||||
})
|
})
|
||||||
class UpgradeApp {
|
class UpgradeApp {
|
||||||
@Input() user: string;
|
@Input() user: string;
|
||||||
|
@ -77,10 +76,12 @@ class UpgradeApp {
|
||||||
}
|
}
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [Pane, UpgradeApp],
|
declarations: [Pane, UpgradeApp, adapter.upgradeNg1Component('ng1User')],
|
||||||
entryComponents: [UpgradeApp]
|
imports: [BrowserModule]
|
||||||
})
|
})
|
||||||
class Ng2AppModule {}
|
class Ng2AppModule {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ng1module.directive('upgradeApp', adapter.downgradeNg2Component(UpgradeApp));
|
ng1module.directive('upgradeApp', adapter.downgradeNg2Component(UpgradeApp));
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/** @experimental */
|
/** @experimental */
|
||||||
export declare class UpgradeAdapter {
|
export declare class UpgradeAdapter {
|
||||||
addProvider(provider: Type | Provider | any[] | any): void;
|
constructor(ng2AppModule?: Type);
|
||||||
|
/** @deprecated */ addProvider(provider: Type | Provider | any[] | any): void;
|
||||||
bootstrap(element: Element, modules?: any[], config?: angular.IAngularBootstrapConfig): UpgradeAdapterRef;
|
bootstrap(element: Element, modules?: any[], config?: angular.IAngularBootstrapConfig): UpgradeAdapterRef;
|
||||||
downgradeNg2Component(type: Type): Function;
|
downgradeNg2Component(type: Type): Function;
|
||||||
downgradeNg2Provider(token: any): Function;
|
downgradeNg2Provider(token: any): Function;
|
||||||
|
|
Loading…
Reference in New Issue