|
| 1 | +///<reference path="../typings/angularjs/angular.d.ts"/> |
| 2 | + |
| 3 | +import { |
| 4 | + platform, |
| 5 | + PlatformRef, |
| 6 | + ApplicationRef, |
| 7 | + ComponentRef, |
| 8 | + bind, |
| 9 | + Directive, |
| 10 | + Component, |
| 11 | + Inject, |
| 12 | + View, |
| 13 | + Type, |
| 14 | + PlatformRef, |
| 15 | + ApplicationRef, |
| 16 | + ChangeDetectorRef, |
| 17 | + AppViewManager, |
| 18 | + NgZone, |
| 19 | + Injector, |
| 20 | + Compiler, |
| 21 | + ProtoViewRef, |
| 22 | + ElementRef, |
| 23 | + HostViewRef, |
| 24 | + ViewRef |
| 25 | +} from 'angular2/angular2'; |
| 26 | +import {applicationDomBindings} from 'angular2/src/core/application_common'; |
| 27 | +import {applicationCommonBindings} from "../../angular2/src/core/application_ref"; |
| 28 | + |
| 29 | +import {getComponentSelector} from './metadata'; |
| 30 | +import {onError} from './util'; |
| 31 | +export const INJECTOR = 'ng2.Injector'; |
| 32 | +export const APP_VIEW_MANAGER = 'ng2.AppViewManager'; |
| 33 | +export const NG2_COMPILER = 'ng2.Compiler'; |
| 34 | +export const NG2_ZONE = 'ng2.NgZone'; |
| 35 | +export const PROTO_VIEW_REF_MAP = 'ng2.ProtoViewRefMap'; |
| 36 | + |
| 37 | +const NG1_REQUIRE_INJECTOR_REF = '$' + INJECTOR + 'Controller'; |
| 38 | +const NG1_SCOPE = '$scope'; |
| 39 | +const NG1_COMPILE = '$compile'; |
| 40 | +const NG1_INJECTOR = '$injector'; |
| 41 | +const REQUIRE_INJECTOR = '^' + INJECTOR; |
| 42 | + |
| 43 | +var moduleCount: number = 0; |
| 44 | +const CAMEL_CASE = /([A-Z])/g; |
| 45 | + |
| 46 | +export function createUpgradeModule(): UpgradeModule { |
| 47 | + var prefix = `NG2_UPGRADE_m${moduleCount++}_`; |
| 48 | + return new UpgradeModule(prefix, angular.module(prefix, [])); |
| 49 | +} |
| 50 | + |
| 51 | + |
| 52 | +export class UpgradeModule { |
| 53 | + componentTypes: Array<Type> = []; |
| 54 | + |
| 55 | + constructor(public idPrefix: string, public ng1Module: angular.IModule) {} |
| 56 | + |
| 57 | + importNg2Component(type: Type): UpgradeModule { |
| 58 | + this.componentTypes.push(type); |
| 59 | + var selector: string = getComponentSelector(type); |
| 60 | + var factory: Function = ng1ComponentDirective(selector, type, `${this.idPrefix}${selector}_c`); |
| 61 | + this.ng1Module.directive(selector, <any[]>factory); |
| 62 | + return this; |
| 63 | + } |
| 64 | + |
| 65 | + exportAsNg2Component(name: string): Type { |
| 66 | + return Directive({ |
| 67 | + selector: name.replace(CAMEL_CASE, (all, next: string) => '-' + next.toLowerCase()) |
| 68 | + }) |
| 69 | + .Class({ |
| 70 | + constructor: [ |
| 71 | + new Inject(NG1_COMPILE), |
| 72 | + new Inject(NG1_SCOPE), |
| 73 | + ElementRef, |
| 74 | + function(compile: angular.ICompileService, scope: angular.IScope, |
| 75 | + elementRef: ElementRef) { compile(elementRef.nativeElement)(scope); } |
| 76 | + ] |
| 77 | + }); |
| 78 | + } |
| 79 | + |
| 80 | + bootstrap(element: Element, modules?: any[], |
| 81 | + config?: angular.IAngularBootstrapConfig): UpgradeRef { |
| 82 | + var upgrade = new UpgradeRef(); |
| 83 | + var ng1Injector: angular.auto.IInjectorService = null; |
| 84 | + var bindings = [ |
| 85 | + applicationCommonBindings(), |
| 86 | + applicationDomBindings(), |
| 87 | + bind(NG1_INJECTOR).toFactory(() => ng1Injector), |
| 88 | + bind(NG1_COMPILE).toFactory(() => ng1Injector.get(NG1_COMPILE)) |
| 89 | + ]; |
| 90 | + |
| 91 | + var platformRef: PlatformRef = platform(); |
| 92 | + var applicationRef: ApplicationRef = platformRef.application(bindings); |
| 93 | + var injector: Injector = applicationRef.injector; |
| 94 | + var ngZone: NgZone = injector.get(NgZone); |
| 95 | + var compiler: Compiler = injector.get(Compiler); |
| 96 | + this.compileNg2Components(compiler).then((protoViewRefMap: ProtoViewRefMap) => { |
| 97 | + ngZone.run(() => { |
| 98 | + this.ng1Module.value(INJECTOR, injector) |
| 99 | + .value(NG2_ZONE, ngZone) |
| 100 | + .value(NG2_COMPILER, compiler) |
| 101 | + .value(PROTO_VIEW_REF_MAP, protoViewRefMap) |
| 102 | + .value(APP_VIEW_MANAGER, injector.get(AppViewManager)) |
| 103 | + .run([ |
| 104 | + '$injector', |
| 105 | + '$rootScope', |
| 106 | + (injector: angular.auto.IInjectorService, rootScope: angular.IRootScopeService) => { |
| 107 | + ng1Injector = injector; |
| 108 | + ngZone.overrideOnTurnDone(() => rootScope.$apply()); |
| 109 | + } |
| 110 | + ]); |
| 111 | + |
| 112 | + modules = modules ? [].concat(modules) : []; |
| 113 | + modules.push(this.idPrefix); |
| 114 | + angular.element(element).data(NG1_REQUIRE_INJECTOR_REF, injector); |
| 115 | + angular.bootstrap(element, modules, config); |
| 116 | + |
| 117 | + upgrade.readyFn && upgrade.readyFn(); |
| 118 | + }); |
| 119 | + }); |
| 120 | + return upgrade; |
| 121 | + } |
| 122 | + |
| 123 | + private compileNg2Components(compiler: Compiler): Promise<ProtoViewRefMap> { |
| 124 | + var promises: Array<Promise<ProtoViewRef>> = []; |
| 125 | + var types = this.componentTypes; |
| 126 | + for (var i = 0; i < types.length; i++) { |
| 127 | + promises.push(compiler.compileInHost(types[i])); |
| 128 | + } |
| 129 | + return Promise.all(promises).then((protoViews: Array<ProtoViewRef>) => { |
| 130 | + var protoViewRefMap: ProtoViewRefMap = {}; |
| 131 | + var types = this.componentTypes; |
| 132 | + for (var i = 0; i < protoViews.length; i++) { |
| 133 | + protoViewRefMap[getComponentSelector(types[i])] = protoViews[i]; |
| 134 | + } |
| 135 | + return protoViewRefMap; |
| 136 | + }, onError); |
| 137 | + } |
| 138 | +} |
| 139 | + |
| 140 | +interface ProtoViewRefMap { |
| 141 | + [selector: string]: ProtoViewRef |
| 142 | +} |
| 143 | + |
| 144 | +function ng1ComponentDirective(selector: string, type: Type, idPrefix: string): Function { |
| 145 | + directiveFactory.$inject = [PROTO_VIEW_REF_MAP, APP_VIEW_MANAGER]; |
| 146 | + function directiveFactory(protoViewRefMap: ProtoViewRefMap, viewManager: AppViewManager): |
| 147 | + angular.IDirective { |
| 148 | + var protoView: ProtoViewRef = protoViewRefMap[selector]; |
| 149 | + if (!protoView) throw new Error('Expecting ProtoViewRef for: ' + selector); |
| 150 | + var idCount = 0; |
| 151 | + return { |
| 152 | + restrict: 'E', |
| 153 | + require: REQUIRE_INJECTOR, |
| 154 | + link: (scope: angular.IScope, element: angular.IAugmentedJQuery, attrs: angular.IAttributes, |
| 155 | + parentInjector: any, transclude: angular.ITranscludeFunction): void => { |
| 156 | + var id = element[0].id = idPrefix + (idCount++); |
| 157 | + var childInjector = parentInjector.resolveAndCreateChild([bind(NG1_SCOPE).toValue(scope)]); |
| 158 | + var hostViewRef = viewManager.createRootHostView(protoView, '#' + id, childInjector); |
| 159 | + var changeDetector: ChangeDetectorRef = hostViewRef.changeDetectorRef; |
| 160 | + scope.$watch(() => changeDetector.detectChanges()); |
| 161 | + element.bind('$remove', () => viewManager.destroyRootHostView(hostViewRef)); |
| 162 | + } |
| 163 | + }; |
| 164 | + } |
| 165 | + return directiveFactory; |
| 166 | +} |
| 167 | + |
| 168 | +export class UpgradeRef { |
| 169 | + readyFn: Function; |
| 170 | + |
| 171 | + ready(fn: Function) { this.readyFn = fn; } |
| 172 | +} |
0 commit comments