|
1 |
| -import {ComponentRef} from 'angular2/src/core/compiler/dynamic_component_loader'; |
2 |
| -import {Injector} from 'angular2/src/core/di'; |
3 |
| -import {Type} from 'angular2/src/core/facade/lang'; |
| 1 | + |
| 2 | +import {NgZone} from 'angular2/src/core/zone/ng_zone'; |
| 3 | +import {Type, isBlank, isPresent, assertionsEnabled} from 'angular2/src/core/facade/lang'; |
| 4 | +import {bind, Binding, Injector, OpaqueToken} from 'angular2/src/core/di'; |
| 5 | +import {APP_COMPONENT_REF_PROMISE, APP_COMPONENT} from './application_tokens'; |
| 6 | +import {Promise, PromiseWrapper, PromiseCompleter} from 'angular2/src/core/facade/async'; |
| 7 | +import {ListWrapper} from 'angular2/src/core/facade/collection'; |
| 8 | +import {Reflector, reflector} from 'angular2/src/core/reflection/reflection'; |
| 9 | +import {TestabilityRegistry, Testability} from 'angular2/src/core/testability/testability'; |
| 10 | +import { |
| 11 | + ComponentRef, |
| 12 | + DynamicComponentLoader |
| 13 | +} from 'angular2/src/core/compiler/dynamic_component_loader'; |
| 14 | +import { |
| 15 | + BaseException, |
| 16 | + WrappedException, |
| 17 | + ExceptionHandler |
| 18 | +} from 'angular2/src/core/facade/exceptions'; |
| 19 | +import {DOM} from 'angular2/src/core/dom/dom_adapter'; |
| 20 | +import {internalView} from 'angular2/src/core/compiler/view_ref'; |
| 21 | +import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle'; |
| 22 | +import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory'; |
| 23 | +import { |
| 24 | + Parser, |
| 25 | + Lexer, |
| 26 | + ChangeDetection, |
| 27 | + DynamicChangeDetection, |
| 28 | + JitChangeDetection, |
| 29 | + PreGeneratedChangeDetection, |
| 30 | + IterableDiffers, |
| 31 | + defaultIterableDiffers, |
| 32 | + KeyValueDiffers, |
| 33 | + defaultKeyValueDiffers |
| 34 | +} from 'angular2/src/core/change_detection/change_detection'; |
| 35 | +import {AppViewPool, APP_VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_pool'; |
| 36 | +import {AppViewManager} from 'angular2/src/core/compiler/view_manager'; |
| 37 | +import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils'; |
| 38 | +import {AppViewListener} from 'angular2/src/core/compiler/view_listener'; |
| 39 | +import {Compiler, CompilerCache} from './compiler/compiler'; |
| 40 | +import {DEFAULT_PIPES} from 'angular2/src/core/pipes'; |
| 41 | +import {ViewResolver} from './compiler/view_resolver'; |
| 42 | +import {DirectiveResolver} from './compiler/directive_resolver'; |
| 43 | +import {PipeResolver} from './compiler/pipe_resolver'; |
| 44 | +import {StyleUrlResolver} from 'angular2/src/core/render/dom/compiler/style_url_resolver'; |
| 45 | +import {UrlResolver} from 'angular2/src/core/services/url_resolver'; |
| 46 | +import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper'; |
| 47 | + |
| 48 | + |
| 49 | +/** |
| 50 | + * Contains everything that is safe to share between applications. |
| 51 | + */ |
| 52 | +export function rootBindings(): Array<Type | Binding | any[]> { |
| 53 | + return [bind(Reflector).toValue(reflector), TestabilityRegistry]; |
| 54 | +} |
| 55 | + |
| 56 | +/** |
| 57 | + * Construct bindings specific to an individual root component. |
| 58 | + */ |
| 59 | +function _componentBindings(appComponentType: Type): Array<Type | Binding | any[]> { |
| 60 | + return [ |
| 61 | + bind(APP_COMPONENT) |
| 62 | + .toValue(appComponentType), |
| 63 | + bind(APP_COMPONENT_REF_PROMISE) |
| 64 | + .toFactory( |
| 65 | + (dynamicComponentLoader, injector: Injector) => { |
| 66 | + // TODO(rado): investigate whether to support bindings on root component. |
| 67 | + return dynamicComponentLoader.loadAsRoot(appComponentType, null, injector) |
| 68 | + .then((componentRef) => { |
| 69 | + if (isPresent(componentRef.location.nativeElement)) { |
| 70 | + injector.get(TestabilityRegistry) |
| 71 | + .registerApplication(componentRef.location.nativeElement, |
| 72 | + injector.get(Testability)); |
| 73 | + } |
| 74 | + return componentRef; |
| 75 | + }); |
| 76 | + }, |
| 77 | + [DynamicComponentLoader, Injector]), |
| 78 | + |
| 79 | + bind(appComponentType) |
| 80 | + .toFactory((p: Promise<any>) => p.then(ref => ref.instance), [APP_COMPONENT_REF_PROMISE]), |
| 81 | + ]; |
| 82 | +} |
| 83 | + |
| 84 | +/** |
| 85 | + * Construct a default set of bindings which should be included in any Angular |
| 86 | + * application, regardless of whether it runs on the UI thread or in a web worker. |
| 87 | + */ |
| 88 | +export function applicationCommonBindings(): Array<Type | Binding | any[]> { |
| 89 | + var bestChangeDetection = new DynamicChangeDetection(); |
| 90 | + if (PreGeneratedChangeDetection.isSupported()) { |
| 91 | + bestChangeDetection = new PreGeneratedChangeDetection(); |
| 92 | + } else if (JitChangeDetection.isSupported()) { |
| 93 | + bestChangeDetection = new JitChangeDetection(); |
| 94 | + } |
| 95 | + return [ |
| 96 | + ProtoViewFactory, |
| 97 | + AppViewPool, |
| 98 | + bind(APP_VIEW_POOL_CAPACITY).toValue(10000), |
| 99 | + AppViewManager, |
| 100 | + AppViewManagerUtils, |
| 101 | + AppViewListener, |
| 102 | + Compiler, |
| 103 | + CompilerCache, |
| 104 | + ViewResolver, |
| 105 | + DEFAULT_PIPES, |
| 106 | + bind(IterableDiffers).toValue(defaultIterableDiffers), |
| 107 | + bind(KeyValueDiffers).toValue(defaultKeyValueDiffers), |
| 108 | + bind(ChangeDetection).toValue(bestChangeDetection), |
| 109 | + DirectiveResolver, |
| 110 | + UrlResolver, |
| 111 | + StyleUrlResolver, |
| 112 | + PipeResolver, |
| 113 | + ComponentUrlMapper, |
| 114 | + Parser, |
| 115 | + Lexer, |
| 116 | + DynamicComponentLoader, |
| 117 | + bind(LifeCycle).toFactory((exceptionHandler) => new LifeCycle(null, assertionsEnabled()), |
| 118 | + [ExceptionHandler]), |
| 119 | + ]; |
| 120 | +} |
| 121 | + |
| 122 | +/** |
| 123 | + * Create an Angular zone. |
| 124 | + */ |
| 125 | +export function createNgZone(): NgZone { |
| 126 | + return new NgZone({enableLongStackTrace: assertionsEnabled()}); |
| 127 | +} |
| 128 | + |
| 129 | +var _platform: PlatformRef; |
| 130 | + |
4 | 131 | /**
|
5 |
| - * Represents a Angular's representation of an Application. |
| 132 | + * @private |
| 133 | + */ |
| 134 | +export function platformCommon(bindings?: Array<Type | Binding | any[]>, initializer?: () => void): |
| 135 | + PlatformRef { |
| 136 | + if (isPresent(_platform)) { |
| 137 | + if (isBlank(bindings)) { |
| 138 | + return _platform; |
| 139 | + } |
| 140 | + throw "platform() can only be called once per page"; |
| 141 | + } |
| 142 | + |
| 143 | + if (isPresent(initializer)) { |
| 144 | + initializer(); |
| 145 | + } |
| 146 | + |
| 147 | + if (isBlank(bindings)) { |
| 148 | + bindings = rootBindings(); |
| 149 | + } |
| 150 | + _platform = new PlatformRef(Injector.resolveAndCreate(bindings), () => { _platform = null; }); |
| 151 | + return _platform; |
| 152 | +} |
| 153 | + |
| 154 | +/** |
| 155 | + * Represent the Angular context on a page, and is a true singleton. |
6 | 156 | *
|
7 |
| - * `ApplicationRef` represents a running application instance. Use it to retrieve the host |
8 |
| - * component, injector, |
9 |
| - * or dispose of an application. |
| 157 | + * The platform {@link Injector} injects dependencies which are also |
| 158 | + * truly singletons in the context of a page (such as the browser's |
| 159 | + * cookie jar). |
10 | 160 | */
|
11 |
| -export class ApplicationRef { |
12 |
| - _hostComponent: ComponentRef; |
13 |
| - _injector: Injector; |
14 |
| - _hostComponentType: Type; |
| 161 | +export class PlatformRef { |
| 162 | + /** |
| 163 | + * @private |
| 164 | + */ |
| 165 | + _applications: ApplicationRef[] = []; |
15 | 166 |
|
16 | 167 | /**
|
17 | 168 | * @private
|
18 | 169 | */
|
19 |
| - constructor(hostComponent: ComponentRef, hostComponentType: Type, injector: Injector) { |
20 |
| - this._hostComponent = hostComponent; |
21 |
| - this._injector = injector; |
22 |
| - this._hostComponentType = hostComponentType; |
23 |
| - } |
| 170 | + constructor(private _injector: Injector, private _dispose: () => void) {} |
| 171 | + |
| 172 | + /** |
| 173 | + * Get the platform {@link Injector}. |
| 174 | + */ |
| 175 | + get injector(): Injector { return this._injector; } |
24 | 176 |
|
25 | 177 | /**
|
26 |
| - * Returns the current {@link ComponentMetadata} type. |
| 178 | + * Build a new Angular application with the given bindings. The `ApplicationRef` |
| 179 | + * returned can be used to bootstrap one or more root components within the |
| 180 | + * application. |
27 | 181 | */
|
28 |
| - get hostComponentType(): Type { return this._hostComponentType; } |
| 182 | + application(bindings: Array<Type | Binding | any[]>): ApplicationRef { |
| 183 | + var app = this._initApp(createNgZone(), bindings); |
| 184 | + return app; |
| 185 | + } |
29 | 186 |
|
30 | 187 | /**
|
31 |
| - * Returns the current {@link ComponentMetadata} instance. |
| 188 | + * Build a new Angular application from asynchronously provided bindings. |
| 189 | + * |
| 190 | + * Runs the `AsyncLoader` callback in the application `Zone` and constructs |
| 191 | + * a new Application from the bindings provided by the `Promise` it returns. |
32 | 192 | */
|
33 |
| - get hostComponent(): any { return this._hostComponent.instance; } |
| 193 | + asyncApplication(bindingFn: (zone: NgZone) => |
| 194 | + Promise<Array<Type | Binding | any[]>>): Promise<ApplicationRef> { |
| 195 | + var zone = createNgZone(); |
| 196 | + var completer = PromiseWrapper.completer(); |
| 197 | + zone.run(() => { |
| 198 | + PromiseWrapper.then(bindingFn(zone), (bindings: Array<Type | Binding | any[]>) => { |
| 199 | + completer.resolve(this._initApp(zone, bindings)); |
| 200 | + }); |
| 201 | + }); |
| 202 | + return completer.promise; |
| 203 | + } |
| 204 | + |
| 205 | + private _initApp(zone: NgZone, bindings: Array<Type | Binding | any[]>): ApplicationRef { |
| 206 | + var injector: Injector; |
| 207 | + zone.run(() => { |
| 208 | + bindings.push(bind(NgZone).toValue(zone)); |
| 209 | + bindings.push(bind(ApplicationRef).toValue(this)); |
| 210 | + |
| 211 | + var exceptionHandler; |
| 212 | + try { |
| 213 | + injector = this.injector.resolveAndCreateChild(bindings); |
| 214 | + exceptionHandler = injector.get(ExceptionHandler); |
| 215 | + zone.overrideOnErrorHandler((e, s) => exceptionHandler.call(e, s)); |
| 216 | + } catch (e) { |
| 217 | + if (isPresent(exceptionHandler)) { |
| 218 | + exceptionHandler.call(e, e.stack); |
| 219 | + } else { |
| 220 | + DOM.logError(e); |
| 221 | + } |
| 222 | + } |
| 223 | + }); |
| 224 | + var app = new ApplicationRef(this, zone, injector); |
| 225 | + this._applications.push(app); |
| 226 | + return app; |
| 227 | + } |
| 228 | + |
34 | 229 |
|
35 | 230 | /**
|
36 |
| - * Dispose (un-load) the application. |
| 231 | + * Destroy the Angular platform and all Angular applications on the page. |
37 | 232 | */
|
38 | 233 | dispose(): void {
|
39 |
| - // TODO: We also need to clean up the Zone, ... here! |
40 |
| - this._hostComponent.dispose(); |
| 234 | + this._applications.forEach((app) => app.dispose()); |
| 235 | + this._dispose(); |
| 236 | + } |
| 237 | + |
| 238 | + /** |
| 239 | + * @private |
| 240 | + */ |
| 241 | + _applicationDisposed(app: ApplicationRef): void { ListWrapper.remove(this._applications, app); } |
| 242 | +} |
| 243 | + |
| 244 | +/** |
| 245 | + * Represents an Angular application. |
| 246 | + * |
| 247 | + * Use to retrieve the application {@link Injector} or to bootstrap new |
| 248 | + * components at the root of the application. Can also be used to dispose |
| 249 | + * of the entire application and all its loaded components. |
| 250 | + */ |
| 251 | +export class ApplicationRef { |
| 252 | + private _bootstrapListeners: Function[] = []; |
| 253 | + private _rootComponents: ComponentRef[] = []; |
| 254 | + |
| 255 | + /** |
| 256 | + * @private |
| 257 | + */ |
| 258 | + constructor(private _platform: PlatformRef, private _zone: NgZone, private _injector: Injector) {} |
| 259 | + |
| 260 | + /** |
| 261 | + * Register a listener to be called each time a new root component type is bootstrapped. |
| 262 | + */ |
| 263 | + registerBootstrapListener(listener: (ref: ComponentRef) => void): void { |
| 264 | + this._bootstrapListeners.push(listener); |
41 | 265 | }
|
42 | 266 |
|
43 | 267 | /**
|
44 |
| - * Returns the root application {@link Injector}. |
| 268 | + * Bootstrap a new component at the root level of the application, optionally with |
| 269 | + * component specific bindings. |
| 270 | + */ |
| 271 | + bootstrap(componentType: Type, bindings?: Array<Type | Binding | any[]>): Promise<ComponentRef> { |
| 272 | + var completer = PromiseWrapper.completer(); |
| 273 | + this._zone.run(() => { |
| 274 | + var componentBindings = _componentBindings(componentType); |
| 275 | + if (isPresent(bindings)) { |
| 276 | + componentBindings.push(bindings); |
| 277 | + } |
| 278 | + var exceptionHandler = this._injector.get(ExceptionHandler); |
| 279 | + try { |
| 280 | + var injector: Injector = this._injector.resolveAndCreateChild(componentBindings); |
| 281 | + var compRefToken: Promise<ComponentRef> = injector.get(APP_COMPONENT_REF_PROMISE); |
| 282 | + var tick = (componentRef) => { |
| 283 | + var appChangeDetector = internalView(componentRef.hostView).changeDetector; |
| 284 | + var lc = injector.get(LifeCycle); |
| 285 | + lc.registerWith(this._zone, appChangeDetector); |
| 286 | + lc.tick(); |
| 287 | + completer.resolve(componentRef); |
| 288 | + this._rootComponents.push(componentRef); |
| 289 | + this._bootstrapListeners.forEach((listener) => listener(componentRef)); |
| 290 | + }; |
| 291 | + |
| 292 | + var tickResult = PromiseWrapper.then(compRefToken, tick); |
| 293 | + |
| 294 | + PromiseWrapper.then(tickResult, (_) => {}); |
| 295 | + PromiseWrapper.then(tickResult, null, |
| 296 | + (err, stackTrace) => completer.reject(err, stackTrace)); |
| 297 | + } catch (e) { |
| 298 | + exceptionHandler.call(e, e.stack); |
| 299 | + completer.reject(e, e.stack); |
| 300 | + } |
| 301 | + }); |
| 302 | + return completer.promise; |
| 303 | + } |
| 304 | + |
| 305 | + /** |
| 306 | + * Retrieve the application {@link Injector}. |
45 | 307 | */
|
46 | 308 | get injector(): Injector { return this._injector; }
|
| 309 | + |
| 310 | + /** |
| 311 | + * Retrieve the application {@link Zone}. |
| 312 | + */ |
| 313 | + get zone(): NgZone { return this._zone; } |
| 314 | + |
| 315 | + dispose(): void { |
| 316 | + // TODO(alxhub): Dispose of the NgZone. |
| 317 | + this._rootComponents.forEach((ref) => ref.dispose()); |
| 318 | + this._platform._applicationDisposed(this); |
| 319 | + } |
47 | 320 | }
|
0 commit comments