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; | ||||||
|  |           UpgradeNg1ComponentAdapterBuilder.resolve(this.ng1ComponentsToBeUpgraded, injector) | ||||||
|  |               .then(() => { | ||||||
|  |                 // At this point we have ng1 injector and we have lifted ng1 components into ng2, we
 | ||||||
|  |                 // now can bootstrap ng2.
 | ||||||
|  |                 var DynamicNgUpgradeModule = | ||||||
|  |                     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({ |                       ngZone.onMicrotaskEmpty.subscribe({ | ||||||
|             next: (_: any /** TODO #???? */) => |                         next: (_: any) => ngZone.runOutsideAngular(() => rootScope.$evalAsync()) | ||||||
|                       ngZone.runOutsideAngular(() => rootScope.$evalAsync()) |  | ||||||
|                       }); |                       }); | ||||||
|           UpgradeNg1ComponentAdapterBuilder.resolve(this.downgradedComponents, injector) |                     }) | ||||||
|                     .then(resolve, reject); |                     .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)); | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										3
									
								
								tools/public_api_guard/upgrade/index.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								tools/public_api_guard/upgrade/index.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user