diff --git a/modules/angular2/core.dart b/modules/angular2/core.dart index b34bd09c16..74100c943b 100644 --- a/modules/angular2/core.dart +++ b/modules/angular2/core.dart @@ -5,9 +5,9 @@ export 'package:angular2/src/core/util.dart'; export 'package:angular2/src/core/di.dart'; export 'package:angular2/src/core/pipes.dart'; export 'package:angular2/src/core/facade.dart'; -export 'package:angular2/src/core/application_ref.dart'; // Do not export application for dart. Must import from angular2/bootstrap //export 'package:angular2/src/core/application.dart'; +export 'package:angular2/src/core/application_ref.dart'; export 'package:angular2/src/core/services.dart'; export 'package:angular2/src/core/compiler.dart'; export 'package:angular2/src/core/lifecycle.dart'; diff --git a/modules/angular2/src/core/application.dart b/modules/angular2/src/core/application.dart index 0e4b71958f..71bcf77943 100644 --- a/modules/angular2/src/core/application.dart +++ b/modules/angular2/src/core/application.dart @@ -6,9 +6,9 @@ import 'package:angular2/src/core/reflection/reflection.dart' show reflector; import 'package:angular2/src/core/reflection/reflection_capabilities.dart' show ReflectionCapabilities; import 'application_common.dart'; -import 'application_ref.dart'; -export 'application_ref.dart' show ApplicationRef; +import 'package:angular2/src/core/compiler/dynamic_component_loader.dart'; +export 'package:angular2/src/core/compiler/dynamic_component_loader.dart' show ComponentRef; /// Starts an application from a root component. This implementation uses /// mirrors. Angular 2 transformer automatically replaces this method with a @@ -16,7 +16,7 @@ export 'application_ref.dart' show ApplicationRef; /// mirrors and produces a faster and more compact JS code. /// /// See [commonBootstrap] for detailed documentation. -Future bootstrap(Type appComponentType, +Future bootstrap(Type appComponentType, [List componentInjectableBindings]) { reflector.reflectionCapabilities = new ReflectionCapabilities(); return commonBootstrap(appComponentType, componentInjectableBindings); diff --git a/modules/angular2/src/core/application.ts b/modules/angular2/src/core/application.ts index 3431e4b945..049a738e70 100644 --- a/modules/angular2/src/core/application.ts +++ b/modules/angular2/src/core/application.ts @@ -1,3 +1,4 @@ // Public API for Application -export {ApplicationRef} from './application_ref'; export {APP_COMPONENT} from './application_tokens'; +export {platform, commonBootstrap as bootstrap} from './application_common'; +export {PlatformRef, ApplicationRef, rootBindings} from './application_ref'; diff --git a/modules/angular2/src/core/application_common.ts b/modules/angular2/src/core/application_common.ts index 27c4ad97b2..1c626b5332 100644 --- a/modules/angular2/src/core/application_common.ts +++ b/modules/angular2/src/core/application_common.ts @@ -1,4 +1,3 @@ -import {DEFAULT_PIPES} from 'angular2/src/core/pipes'; import {FORM_BINDINGS} from 'angular2/src/core/forms'; import {bind, Binding, Injector, OpaqueToken} from 'angular2/src/core/di'; import { @@ -10,37 +9,12 @@ import { print, stringify } from 'angular2/src/core/facade/lang'; -import { - BaseException, - WrappedException, - ExceptionHandler -} from 'angular2/src/core/facade/exceptions'; import {BrowserDomAdapter} from 'angular2/src/core/dom/browser_adapter'; +import {BrowserGetTestability} from 'angular2/src/core/testability/browser_testability'; import {DOM} from 'angular2/src/core/dom/dom_adapter'; -import {Compiler, CompilerCache} from './compiler/compiler'; -import {Reflector, reflector} from 'angular2/src/core/reflection/reflection'; -import { - Parser, - Lexer, - ChangeDetection, - DynamicChangeDetection, - JitChangeDetection, - PreGeneratedChangeDetection, - IterableDiffers, - defaultIterableDiffers, - KeyValueDiffers, - defaultKeyValueDiffers -} from 'angular2/src/core/change_detection/change_detection'; import {ViewLoader} from 'angular2/src/core/render/dom/compiler/view_loader'; -import {StyleUrlResolver} from 'angular2/src/core/render/dom/compiler/style_url_resolver'; import {StyleInliner} from 'angular2/src/core/render/dom/compiler/style_inliner'; -import {ViewResolver} from './compiler/view_resolver'; -import {DirectiveResolver} from './compiler/directive_resolver'; -import {PipeResolver} from './compiler/pipe_resolver'; -import {ListWrapper} from 'angular2/src/core/facade/collection'; import {Promise, PromiseWrapper, PromiseCompleter} from 'angular2/src/core/facade/async'; -import {NgZone} from 'angular2/src/core/zone/ng_zone'; -import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle'; import {XHR} from 'angular2/src/core/render/xhr'; import {XHRImpl} from 'angular2/src/core/render/xhr_impl'; import { @@ -50,8 +24,6 @@ import { } from 'angular2/src/core/render/dom/events/event_manager'; import {KeyEventsPlugin} from 'angular2/src/core/render/dom/events/key_events'; import {HammerGesturesPlugin} from 'angular2/src/core/render/dom/events/hammer_gestures'; -import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper'; -import {UrlResolver} from 'angular2/src/core/services/url_resolver'; import {AppRootUrl} from 'angular2/src/core/services/app_root_url'; import {AnchorBasedAppRootUrl} from 'angular2/src/core/services/anchor_based_app_root_url'; import { @@ -59,11 +31,6 @@ import { DynamicComponentLoader } from 'angular2/src/core/compiler/dynamic_component_loader'; import {TestabilityRegistry, Testability} from 'angular2/src/core/testability/testability'; -import {AppViewPool, APP_VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_pool'; -import {AppViewManager} from 'angular2/src/core/compiler/view_manager'; -import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils'; -import {AppViewListener} from 'angular2/src/core/compiler/view_listener'; -import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory'; import {Renderer, RenderCompiler} from 'angular2/src/core/render/api'; import { DomRenderer, @@ -81,46 +48,23 @@ import { SharedStylesHost, DomSharedStylesHost } from 'angular2/src/core/render/dom/view/shared_styles_host'; -import {internalView} from 'angular2/src/core/compiler/view_ref'; -import {APP_COMPONENT_REF_PROMISE, APP_COMPONENT} from './application_tokens'; -import {wtfInit} from './profile/wtf_init'; import {EXCEPTION_BINDING} from './platform_bindings'; -import {ApplicationRef} from './application_ref'; import {AnimationBuilder} from 'angular2/src/animate/animation_builder'; import {BrowserDetails} from 'angular2/src/animate/browser_details'; +import {wtfInit} from './profile/wtf_init'; +import {platformCommon, PlatformRef, applicationCommonBindings} from './application_ref'; -var _rootInjector: Injector; - -// Contains everything that is safe to share between applications. -var _rootBindings = [bind(Reflector).toValue(reflector), TestabilityRegistry]; - -function _injectorBindings(appComponentType): Array { - var bestChangeDetection = new DynamicChangeDetection(); - if (PreGeneratedChangeDetection.isSupported()) { - bestChangeDetection = new PreGeneratedChangeDetection(); - } else if (JitChangeDetection.isSupported()) { - bestChangeDetection = new JitChangeDetection(); +/** + * A default set of bindings which apply only to an Angular application running on + * the UI thread. + */ +export function applicationDomBindings(): Array { + if (isBlank(DOM)) { + throw "Must set a root DOM adapter first."; } return [ bind(DOCUMENT) .toValue(DOM.defaultDoc()), - bind(APP_COMPONENT).toValue(appComponentType), - bind(APP_COMPONENT_REF_PROMISE) - .toFactory( - (dynamicComponentLoader, injector, testability, registry) => { - // TODO(rado): investigate whether to support bindings on root component. - return dynamicComponentLoader.loadAsRoot(appComponentType, null, injector) - .then((componentRef) => { - registry.registerApplication(componentRef.location.nativeElement, testability); - return componentRef; - }); - }, - [DynamicComponentLoader, Injector, Testability, TestabilityRegistry]), - - bind(appComponentType) - .toFactory((p: Promise) => p.then(ref => ref.instance), [APP_COMPONENT_REF_PROMISE]), - bind(LifeCycle).toFactory((exceptionHandler) => new LifeCycle(null, assertionsEnabled()), - [ExceptionHandler]), EventManager, new Binding(EVENT_MANAGER_PLUGINS, {toClass: DomEventsPlugin, multi: true}), new Binding(EVENT_MANAGER_PLUGINS, {toClass: KeyEventsPlugin, multi: true}), @@ -135,31 +79,10 @@ function _injectorBindings(appComponentType): Array { bind(RenderCompiler).toAlias(DefaultDomCompiler), DomSharedStylesHost, bind(SharedStylesHost).toAlias(DomSharedStylesHost), - ProtoViewFactory, - AppViewPool, - bind(APP_VIEW_POOL_CAPACITY).toValue(10000), - AppViewManager, - AppViewManagerUtils, - AppViewListener, - Compiler, - CompilerCache, - ViewResolver, - DEFAULT_PIPES, - bind(IterableDiffers).toValue(defaultIterableDiffers), - bind(KeyValueDiffers).toValue(defaultKeyValueDiffers), - bind(ChangeDetection).toValue(bestChangeDetection), ViewLoader, - DirectiveResolver, - PipeResolver, - Parser, - Lexer, EXCEPTION_BINDING, bind(XHR).toValue(new XHRImpl()), - ComponentUrlMapper, - UrlResolver, - StyleUrlResolver, StyleInliner, - DynamicComponentLoader, Testability, AnchorBasedAppRootUrl, bind(AppRootUrl).toAlias(AnchorBasedAppRootUrl), @@ -169,16 +92,28 @@ function _injectorBindings(appComponentType): Array { ]; } -export function createNgZone(): NgZone { - return new NgZone({enableLongStackTrace: assertionsEnabled()}); + + +/** + * Initialize the Angular context on the page. + * + * If no bindings are provided, calling {@link platform}() is idempotent, + * and will use the default platform bindings (which can be obtained from + * {@link rootBindings}). + */ +export function platform(bindings?: Array): PlatformRef { + return platformCommon(bindings, () => { + BrowserDomAdapter.makeCurrent(); + wtfInit(); + BrowserGetTestability.init(); + }); } /** * Bootstrapping for Angular applications. * - * You instantiate an Angular application by explicitly specifying a component to use as the root - * component for your - * application via the `bootstrap()` method. + * You instantiate an Angular application by explicitly specifying a component to use + * as the root component for your application via the `bootstrap()` method. * * ## Simple Example * @@ -193,15 +128,12 @@ export function createNgZone(): NgZone { * * ``` * - * An application is bootstrapped inside an existing browser DOM, typically `index.html`. Unlike - * Angular 1, Angular 2 - * does not compile/process bindings in `index.html`. This is mainly for security reasons, as well - * as architectural - * changes in Angular 2. This means that `index.html` can safely be processed using server-side - * technologies such as - * bindings. Bindings can thus use double-curly `{{ syntax }}` without collision from Angular 2 - * component double-curly - * `{{ syntax }}`. + * An application is bootstrapped inside an existing browser DOM, typically `index.html`. + * Unlike Angular 1, Angular 2 does not compile/process bindings in `index.html`. This is + * mainly for security reasons, as well as architectural changes in Angular 2. This means + * that `index.html` can safely be processed using server-side technologies such as + * bindings. Bindings can thus use double-curly `{{ syntax }}` without collision from + * Angular 2 component double-curly `{{ syntax }}`. * * We can use this script code: * @@ -225,134 +157,77 @@ export function createNgZone(): NgZone { * } * ``` * - * When the app developer invokes `bootstrap()` with the root component `MyApp` as its argument, - * Angular performs the - * following tasks: + * When the app developer invokes `bootstrap()` with the root component `MyApp` as its + * argument, Angular performs the following tasks: * - * 1. It uses the component's `selector` property to locate the DOM element which needs to be - * upgraded into - * the angular component. - * 2. It creates a new child injector (from the platform injector). Optionally, you can also - * override the injector configuration for an app by - * invoking `bootstrap` with the `componentInjectableBindings` argument. - * 3. It creates a new `Zone` and connects it to the angular application's change detection domain - * instance. - * 4. It creates a shadow DOM on the selected component's host element and loads the template into - * it. + * 1. It uses the component's `selector` property to locate the DOM element which needs + * to be upgraded into the angular component. + * 2. It creates a new child injector (from the platform injector). Optionally, you can + * also override the injector configuration for an app by invoking `bootstrap` with the + * `componentInjectableBindings` argument. + * 3. It creates a new `Zone` and connects it to the angular application's change detection + * domain instance. + * 4. It creates a shadow DOM on the selected component's host element and loads the + * template into it. * 5. It instantiates the specified component. * 6. Finally, Angular performs change detection to apply the initial data bindings for the - * application. + * application. * * * ## Instantiating Multiple Applications on a Single Page * * There are two ways to do this. * - * * ### Isolated Applications * - * Angular creates a new application each time that the `bootstrap()` method is invoked. When - * multiple applications - * are created for a page, Angular treats each application as independent within an isolated change - * detection and - * `Zone` domain. If you need to share data between applications, use the strategy described in the - * next - * section, "Applications That Share Change Detection." + * Angular creates a new application each time that the `bootstrap()` method is invoked. + * When multiple applications are created for a page, Angular treats each application as + * independent within an isolated change detection and `Zone` domain. If you need to share + * data between applications, use the strategy described in the next section, "Applications + * That Share Change Detection." * * * ### Applications That Share Change Detection * - * If you need to bootstrap multiple applications that share common data, the applications must - * share a common - * change detection and zone. To do that, create a meta-component that lists the application - * components in its template. - * By only invoking the `bootstrap()` method once, with the meta-component as its argument, you - * ensure that only a - * single change detection zone is created and therefore data can be shared across the applications. + * If you need to bootstrap multiple applications that share common data, the applications + * must share a common change detection and zone. To do that, create a meta-component that + * lists the application components in its template. + * + * By only invoking the `bootstrap()` method once, with the meta-component as its argument, + * you ensure that only a single change detection zone is created and therefore data can be + * shared across the applications. * * * ## Platform Injector * * When working within a browser window, there are many singleton resources: cookies, title, - * location, and others. - * Angular services that represent these resources must likewise be shared across all Angular - * applications that - * occupy the same browser window. For this reason, Angular creates exactly one global platform - * injector which stores - * all shared services, and each angular application injector has the platform injector as its - * parent. + * location, and others. Angular services that represent these resources must likewise be + * shared across all Angular applications that occupy the same browser window. For this + * reason, Angular creates exactly one global platform injector which stores all shared + * services, and each angular application injector has the platform injector as its parent. * - * Each application has its own private injector as well. When there are multiple applications on a - * page, Angular treats - * each application injector's services as private to that application. + * Each application has its own private injector as well. When there are multipl + * applications on a page, Angular treats each application injector's services as private + * to that application. * * * # API - * - `appComponentType`: The root component which should act as the application. This is a reference - * to a `Type` - * which is annotated with `@Component(...)`. - * - `componentInjectableBindings`: An additional set of bindings that can be added to the app - * injector - * to override default injection behavior. - * - `errorReporter`: `function(exception:any, stackTrace:string)` a default error reporter for - * unhandled exceptions. + * - `appComponentType`: The root component which should act as the application. This is + * a reference to a `Type` which is annotated with `@Component(...)`. + * - `componentInjectableBindings`: An additional set of bindings that can be added to the + * app injector to override default injection behavior. + * - `errorReporter`: `function(exception:any, stackTrace:string)` a default error reporter + * for unhandled exceptions. * * Returns a `Promise` of {@link ApplicationRef}. */ export function commonBootstrap(appComponentType: /*Type*/ any, - componentInjectableBindings: Array = null): - Promise { - BrowserDomAdapter.makeCurrent(); - wtfInit(); - var bootstrapProcess = PromiseWrapper.completer(); - var zone = createNgZone(); - - zone.run(() => { - var exceptionHandler; - - try { - var appInjector = _createAppInjector(appComponentType, componentInjectableBindings, zone); - exceptionHandler = appInjector.get(ExceptionHandler); - zone.overrideOnErrorHandler((e, s) => exceptionHandler.call(e, s)); - - var compRefToken: Promise = appInjector.get(APP_COMPONENT_REF_PROMISE); - var tick = (componentRef) => { - var appChangeDetector = internalView(componentRef.hostView).changeDetector; - // retrieve life cycle: may have already been created if injected in root component - var lc = appInjector.get(LifeCycle); - lc.registerWith(zone, appChangeDetector); - lc.tick(); // the first tick that will bootstrap the app - - bootstrapProcess.resolve(new ApplicationRef(componentRef, appComponentType, appInjector)); - }; - - var tickResult = PromiseWrapper.then(compRefToken, tick); - - PromiseWrapper.then(tickResult, - (_) => {}); // required for Dart to trigger the default error handler - PromiseWrapper.then(tickResult, null, - (err, stackTrace) => { bootstrapProcess.reject(err, stackTrace); }); - } catch (e) { - if (isPresent(exceptionHandler)) { - exceptionHandler.call(e, e.stack); - } else { - // The error happened during the creation of an injector, most likely because of a bug in - // DI. - // We cannot use the provided exception handler, so we default to writing to the DOM. - DOM.logError(e); - } - bootstrapProcess.reject(e, e.stack); - } - }); - return bootstrapProcess.promise; -} - -function _createAppInjector(appComponentType: Type, bindings: Array, - zone: NgZone): Injector { - if (isBlank(_rootInjector)) _rootInjector = Injector.resolveAndCreate(_rootBindings); - var mergedBindings: any[] = - isPresent(bindings) ? ListWrapper.concat(_injectorBindings(appComponentType), bindings) : - _injectorBindings(appComponentType); - mergedBindings.push(bind(NgZone).toValue(zone)); - return _rootInjector.resolveAndCreateChild(mergedBindings); + appBindings: Array = null): + Promise { + var p = platform(); + var bindings = [applicationCommonBindings(), applicationDomBindings()]; + if (isPresent(appBindings)) { + bindings.push(appBindings); + } + return p.application(bindings).bootstrap(appComponentType); } diff --git a/modules/angular2/src/core/application_ref.ts b/modules/angular2/src/core/application_ref.ts index 92e750e878..e23ed3a107 100644 --- a/modules/angular2/src/core/application_ref.ts +++ b/modules/angular2/src/core/application_ref.ts @@ -1,47 +1,320 @@ -import {ComponentRef} from 'angular2/src/core/compiler/dynamic_component_loader'; -import {Injector} from 'angular2/src/core/di'; -import {Type} from 'angular2/src/core/facade/lang'; + +import {NgZone} from 'angular2/src/core/zone/ng_zone'; +import {Type, isBlank, isPresent, assertionsEnabled} from 'angular2/src/core/facade/lang'; +import {bind, Binding, Injector, OpaqueToken} from 'angular2/src/core/di'; +import {APP_COMPONENT_REF_PROMISE, APP_COMPONENT} from './application_tokens'; +import {Promise, PromiseWrapper, PromiseCompleter} from 'angular2/src/core/facade/async'; +import {ListWrapper} from 'angular2/src/core/facade/collection'; +import {Reflector, reflector} from 'angular2/src/core/reflection/reflection'; +import {TestabilityRegistry, Testability} from 'angular2/src/core/testability/testability'; +import { + ComponentRef, + DynamicComponentLoader +} from 'angular2/src/core/compiler/dynamic_component_loader'; +import { + BaseException, + WrappedException, + ExceptionHandler +} from 'angular2/src/core/facade/exceptions'; +import {DOM} from 'angular2/src/core/dom/dom_adapter'; +import {internalView} from 'angular2/src/core/compiler/view_ref'; +import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle'; +import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory'; +import { + Parser, + Lexer, + ChangeDetection, + DynamicChangeDetection, + JitChangeDetection, + PreGeneratedChangeDetection, + IterableDiffers, + defaultIterableDiffers, + KeyValueDiffers, + defaultKeyValueDiffers +} from 'angular2/src/core/change_detection/change_detection'; +import {AppViewPool, APP_VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_pool'; +import {AppViewManager} from 'angular2/src/core/compiler/view_manager'; +import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils'; +import {AppViewListener} from 'angular2/src/core/compiler/view_listener'; +import {Compiler, CompilerCache} from './compiler/compiler'; +import {DEFAULT_PIPES} from 'angular2/src/core/pipes'; +import {ViewResolver} from './compiler/view_resolver'; +import {DirectiveResolver} from './compiler/directive_resolver'; +import {PipeResolver} from './compiler/pipe_resolver'; +import {StyleUrlResolver} from 'angular2/src/core/render/dom/compiler/style_url_resolver'; +import {UrlResolver} from 'angular2/src/core/services/url_resolver'; +import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper'; + + /** - * Represents a Angular's representation of an Application. - * - * `ApplicationRef` represents a running application instance. Use it to retrieve the host - * component, injector, - * or dispose of an application. + * Contains everything that is safe to share between applications. */ -export class ApplicationRef { - _hostComponent: ComponentRef; - _injector: Injector; - _hostComponentType: Type; +export function rootBindings(): Array { + return [bind(Reflector).toValue(reflector), TestabilityRegistry]; +} + +/** + * Construct bindings specific to an individual root component. + */ +function _componentBindings(appComponentType: Type): Array { + return [ + bind(APP_COMPONENT) + .toValue(appComponentType), + bind(APP_COMPONENT_REF_PROMISE) + .toFactory( + (dynamicComponentLoader, injector: Injector) => { + // TODO(rado): investigate whether to support bindings on root component. + return dynamicComponentLoader.loadAsRoot(appComponentType, null, injector) + .then((componentRef) => { + if (isPresent(componentRef.location.nativeElement)) { + injector.get(TestabilityRegistry) + .registerApplication(componentRef.location.nativeElement, + injector.get(Testability)); + } + return componentRef; + }); + }, + [DynamicComponentLoader, Injector]), + + bind(appComponentType) + .toFactory((p: Promise) => p.then(ref => ref.instance), [APP_COMPONENT_REF_PROMISE]), + ]; +} + +/** + * Construct a default set of bindings which should be included in any Angular + * application, regardless of whether it runs on the UI thread or in a web worker. + */ +export function applicationCommonBindings(): Array { + var bestChangeDetection = new DynamicChangeDetection(); + if (PreGeneratedChangeDetection.isSupported()) { + bestChangeDetection = new PreGeneratedChangeDetection(); + } else if (JitChangeDetection.isSupported()) { + bestChangeDetection = new JitChangeDetection(); + } + return [ + ProtoViewFactory, + AppViewPool, + bind(APP_VIEW_POOL_CAPACITY).toValue(10000), + AppViewManager, + AppViewManagerUtils, + AppViewListener, + Compiler, + CompilerCache, + ViewResolver, + DEFAULT_PIPES, + bind(IterableDiffers).toValue(defaultIterableDiffers), + bind(KeyValueDiffers).toValue(defaultKeyValueDiffers), + bind(ChangeDetection).toValue(bestChangeDetection), + DirectiveResolver, + UrlResolver, + StyleUrlResolver, + PipeResolver, + ComponentUrlMapper, + Parser, + Lexer, + DynamicComponentLoader, + bind(LifeCycle).toFactory((exceptionHandler) => new LifeCycle(null, assertionsEnabled()), + [ExceptionHandler]), + ]; +} + +/** + * Create an Angular zone. + */ +export function createNgZone(): NgZone { + return new NgZone({enableLongStackTrace: assertionsEnabled()}); +} + +var _platform: PlatformRef; + +/** + * @private + */ +export function platformCommon(bindings?: Array, initializer?: () => void): + PlatformRef { + if (isPresent(_platform)) { + if (isBlank(bindings)) { + return _platform; + } + throw "platform() can only be called once per page"; + } + + if (isPresent(initializer)) { + initializer(); + } + + if (isBlank(bindings)) { + bindings = rootBindings(); + } + _platform = new PlatformRef(Injector.resolveAndCreate(bindings), () => { _platform = null; }); + return _platform; +} + +/** + * Represent the Angular context on a page, and is a true singleton. + * + * The platform {@link Injector} injects dependencies which are also + * truly singletons in the context of a page (such as the browser's + * cookie jar). + */ +export class PlatformRef { + /** + * @private + */ + _applications: ApplicationRef[] = []; /** * @private */ - constructor(hostComponent: ComponentRef, hostComponentType: Type, injector: Injector) { - this._hostComponent = hostComponent; - this._injector = injector; - this._hostComponentType = hostComponentType; - } + constructor(private _injector: Injector, private _dispose: () => void) {} /** - * Returns the current {@link ComponentMetadata} type. - */ - get hostComponentType(): Type { return this._hostComponentType; } - - /** - * Returns the current {@link ComponentMetadata} instance. - */ - get hostComponent(): any { return this._hostComponent.instance; } - - /** - * Dispose (un-load) the application. - */ - dispose(): void { - // TODO: We also need to clean up the Zone, ... here! - this._hostComponent.dispose(); - } - - /** - * Returns the root application {@link Injector}. + * Get the platform {@link Injector}. */ get injector(): Injector { return this._injector; } + + /** + * Build a new Angular application with the given bindings. The `ApplicationRef` + * returned can be used to bootstrap one or more root components within the + * application. + */ + application(bindings: Array): ApplicationRef { + var app = this._initApp(createNgZone(), bindings); + return app; + } + + /** + * Build a new Angular application from asynchronously provided bindings. + * + * Runs the `AsyncLoader` callback in the application `Zone` and constructs + * a new Application from the bindings provided by the `Promise` it returns. + */ + asyncApplication(bindingFn: (zone: NgZone) => + Promise>): Promise { + var zone = createNgZone(); + var completer = PromiseWrapper.completer(); + zone.run(() => { + PromiseWrapper.then(bindingFn(zone), (bindings: Array) => { + completer.resolve(this._initApp(zone, bindings)); + }); + }); + return completer.promise; + } + + private _initApp(zone: NgZone, bindings: Array): ApplicationRef { + var injector: Injector; + zone.run(() => { + bindings.push(bind(NgZone).toValue(zone)); + bindings.push(bind(ApplicationRef).toValue(this)); + + var exceptionHandler; + try { + injector = this.injector.resolveAndCreateChild(bindings); + exceptionHandler = injector.get(ExceptionHandler); + zone.overrideOnErrorHandler((e, s) => exceptionHandler.call(e, s)); + } catch (e) { + if (isPresent(exceptionHandler)) { + exceptionHandler.call(e, e.stack); + } else { + DOM.logError(e); + } + } + }); + var app = new ApplicationRef(this, zone, injector); + this._applications.push(app); + return app; + } + + + /** + * Destroy the Angular platform and all Angular applications on the page. + */ + dispose(): void { + this._applications.forEach((app) => app.dispose()); + this._dispose(); + } + + /** + * @private + */ + _applicationDisposed(app: ApplicationRef): void { ListWrapper.remove(this._applications, app); } +} + +/** + * Represents an Angular application. + * + * Use to retrieve the application {@link Injector} or to bootstrap new + * components at the root of the application. Can also be used to dispose + * of the entire application and all its loaded components. + */ +export class ApplicationRef { + private _bootstrapListeners: Function[] = []; + private _rootComponents: ComponentRef[] = []; + + /** + * @private + */ + constructor(private _platform: PlatformRef, private _zone: NgZone, private _injector: Injector) {} + + /** + * Register a listener to be called each time a new root component type is bootstrapped. + */ + registerBootstrapListener(listener: (ref: ComponentRef) => void): void { + this._bootstrapListeners.push(listener); + } + + /** + * Bootstrap a new component at the root level of the application, optionally with + * component specific bindings. + */ + bootstrap(componentType: Type, bindings?: Array): Promise { + var completer = PromiseWrapper.completer(); + this._zone.run(() => { + var componentBindings = _componentBindings(componentType); + if (isPresent(bindings)) { + componentBindings.push(bindings); + } + var exceptionHandler = this._injector.get(ExceptionHandler); + try { + var injector: Injector = this._injector.resolveAndCreateChild(componentBindings); + var compRefToken: Promise = injector.get(APP_COMPONENT_REF_PROMISE); + var tick = (componentRef) => { + var appChangeDetector = internalView(componentRef.hostView).changeDetector; + var lc = injector.get(LifeCycle); + lc.registerWith(this._zone, appChangeDetector); + lc.tick(); + completer.resolve(componentRef); + this._rootComponents.push(componentRef); + this._bootstrapListeners.forEach((listener) => listener(componentRef)); + }; + + var tickResult = PromiseWrapper.then(compRefToken, tick); + + PromiseWrapper.then(tickResult, (_) => {}); + PromiseWrapper.then(tickResult, null, + (err, stackTrace) => completer.reject(err, stackTrace)); + } catch (e) { + exceptionHandler.call(e, e.stack); + completer.reject(e, e.stack); + } + }); + return completer.promise; + } + + /** + * Retrieve the application {@link Injector}. + */ + get injector(): Injector { return this._injector; } + + /** + * Retrieve the application {@link Zone}. + */ + get zone(): NgZone { return this._zone; } + + dispose(): void { + // TODO(alxhub): Dispose of the NgZone. + this._rootComponents.forEach((ref) => ref.dispose()); + this._platform._applicationDisposed(this); + } } diff --git a/modules/angular2/src/core/application_static.dart b/modules/angular2/src/core/application_static.dart index bf1ec9aa74..fd71062bfb 100644 --- a/modules/angular2/src/core/application_static.dart +++ b/modules/angular2/src/core/application_static.dart @@ -2,12 +2,12 @@ library angular2.src.core.application_static; import 'dart:async'; import 'application_common.dart'; -import 'application_ref.dart'; +import 'package:angular2/src/core/compiler/dynamic_component_loader.dart' show ComponentRef; /// Starts an application from a root component. /// /// See [commonBootstrap] for detailed documentation. -Future bootstrapStatic(Type appComponentType, +Future bootstrapStatic(Type appComponentType, [List componentInjectableBindings, void initReflector()]) { if (initReflector != null) { initReflector(); diff --git a/modules/angular2/src/core/bootstrap.dart b/modules/angular2/src/core/bootstrap.dart index 447b11c7ad..4a1340d4c5 100644 --- a/modules/angular2/src/core/bootstrap.dart +++ b/modules/angular2/src/core/bootstrap.dart @@ -2,7 +2,7 @@ library angular2.src.core.bootstrap; import 'dart:async'; -import 'application_ref.dart'; +import 'package:angular2/src/core/compiler/dynamic_component_loader.dart' show ComponentRef; import 'package:angular2/src/core/reflection/reflection.dart' show reflector; import 'package:angular2/src/core/reflection/reflection_capabilities.dart' show ReflectionCapabilities; @@ -14,7 +14,7 @@ import 'application_common.dart'; /// mirrors and produces a faster and more compact JS code. /// /// See [commonBootstrap] for detailed documentation. -Future bootstrap(Type appComponentType, +Future bootstrap(Type appComponentType, [List componentInjectableBindings]) { reflector.reflectionCapabilities = new ReflectionCapabilities(); return commonBootstrap(appComponentType, componentInjectableBindings); diff --git a/modules/angular2/src/core/compiler/dynamic_component_loader.ts b/modules/angular2/src/core/compiler/dynamic_component_loader.ts index fc7a1de7ce..3f773e75bf 100644 --- a/modules/angular2/src/core/compiler/dynamic_component_loader.ts +++ b/modules/angular2/src/core/compiler/dynamic_component_loader.ts @@ -1,11 +1,15 @@ import {Key, Injector, ResolvedBinding, Binding, bind, Injectable} from 'angular2/src/core/di'; import {Compiler} from './compiler'; -import {Type, stringify, isPresent} from 'angular2/src/core/facade/lang'; +import {isType, Type, stringify, isPresent} from 'angular2/src/core/facade/lang'; import {Promise} from 'angular2/src/core/facade/async'; import {AppViewManager} from 'angular2/src/core/compiler/view_manager'; import {ElementRef} from './element_ref'; import {ViewRef, HostViewRef} from './view_ref'; +function _asType(typeOrBinding: Type | Binding): Type { + return isType(typeOrBinding) ? typeOrBinding : (typeOrBinding).token; +} + /** * Angular's reference to a component instance. * @@ -22,12 +26,20 @@ export class ComponentRef { */ instance: any; + componentType: Type; + + + injector: Injector; + /** * @private */ - constructor(location: ElementRef, instance: any, private _dispose: () => void) { + constructor(location: ElementRef, instance: any, componentType: Type, injector: Injector, + private _dispose: () => void) { this.location = location; this.instance = instance; + this.componentType = componentType; + this.injector = injector; } /** @@ -35,6 +47,10 @@ export class ComponentRef { */ get hostView(): HostViewRef { return this.location.parentView; } + get hostComponentType(): Type { return this.componentType; } + + get hostComponent(): any { return this.instance; } + /** * Dispose of the component instance. */ @@ -104,8 +120,8 @@ export class DynamicComponentLoader { * * ``` */ - loadAsRoot(typeOrBinding: Type | Binding, overrideSelector: string, - injector: Injector): Promise { + loadAsRoot(typeOrBinding: Type | Binding, overrideSelector: string, injector: Injector, + onDispose?: () => void): Promise { return this._compiler.compileInHost(typeOrBinding) .then(hostProtoViewRef => { var hostViewRef = @@ -113,8 +129,14 @@ export class DynamicComponentLoader { var newLocation = this._viewManager.getHostElement(hostViewRef); var component = this._viewManager.getComponent(newLocation); - var dispose = () => { this._viewManager.destroyRootHostView(hostViewRef); }; - return new ComponentRef(newLocation, component, dispose); + var dispose = () => { + this._viewManager.destroyRootHostView(hostViewRef); + if (isPresent(onDispose)) { + onDispose(); + } + }; + return new ComponentRef(newLocation, component, _asType(typeOrBinding), injector, + dispose); }); } @@ -229,7 +251,7 @@ export class DynamicComponentLoader { viewContainer.remove(index); } }; - return new ComponentRef(newLocation, component, dispose); + return new ComponentRef(newLocation, component, _asType(typeOrBinding), null, dispose); }); } } diff --git a/modules/angular2/src/core/testability/get_testability.dart b/modules/angular2/src/core/testability/browser_testability.dart similarity index 91% rename from modules/angular2/src/core/testability/get_testability.dart rename to modules/angular2/src/core/testability/browser_testability.dart index bfe3375083..fb4d2c09b0 100644 --- a/modules/angular2/src/core/testability/get_testability.dart +++ b/modules/angular2/src/core/testability/browser_testability.dart @@ -1,4 +1,4 @@ -library testability.get_testability; +library testability.browser_testability; import './testability.dart'; @@ -80,8 +80,14 @@ class PublicTestability implements _JsObjectProxyable { } } -class GetTestability { - static addToWindow(TestabilityRegistry registry) { +class BrowserGetTestability implements GetTestability { + const BrowserGetTestability(); + + static init() { + setTestabilityGetter(const BrowserGetTestability()); + } + + void addToWindow(TestabilityRegistry registry) { var jsRegistry = js.context['ngTestabilityRegistries']; if (jsRegistry == null) { js.context['ngTestabilityRegistries'] = jsRegistry = new js.JsArray(); @@ -106,10 +112,10 @@ class GetTestability { return _jsify(result); }); } - jsRegistry.add(_createRegistry(registry)); + jsRegistry.add(this._createRegistry(registry)); } - static js.JsObject _createRegistry(TestabilityRegistry registry) { + js.JsObject _createRegistry(TestabilityRegistry registry) { var object = new js.JsObject(js.context['Object']); object['getAngularTestability'] = _jsify((Element elem, bool findInAncestors) { diff --git a/modules/angular2/src/core/testability/get_testability.ts b/modules/angular2/src/core/testability/browser_testability.ts similarity index 76% rename from modules/angular2/src/core/testability/get_testability.ts rename to modules/angular2/src/core/testability/browser_testability.ts index 0ea9dcea88..de9fcefc4e 100644 --- a/modules/angular2/src/core/testability/get_testability.ts +++ b/modules/angular2/src/core/testability/browser_testability.ts @@ -1,4 +1,9 @@ -import {TestabilityRegistry, Testability} from 'angular2/src/core/testability/testability'; +import { + TestabilityRegistry, + Testability, + GetTestability, + setTestabilityGetter +} from 'angular2/src/core/testability/testability'; import {global} from 'angular2/src/core/facade/lang'; class PublicTestability { @@ -13,8 +18,10 @@ class PublicTestability { } } -export class GetTestability { - static addToWindow(registry: TestabilityRegistry) { +export class BrowserGetTestability implements GetTestability { + static init() { setTestabilityGetter(new BrowserGetTestability()); } + + addToWindow(registry: TestabilityRegistry): void { global.getAngularTestability = function(elem: Element, findInAncestors: boolean = true): PublicTestability { var testability = registry.findTestabilityInTree(elem, findInAncestors); diff --git a/modules/angular2/src/core/testability/testability.ts b/modules/angular2/src/core/testability/testability.ts index a74f22e6fc..8454e810fb 100644 --- a/modules/angular2/src/core/testability/testability.ts +++ b/modules/angular2/src/core/testability/testability.ts @@ -1,8 +1,8 @@ import {Injectable} from 'angular2/src/core/di'; import {DOM} from 'angular2/src/core/dom/dom_adapter'; import {Map, MapWrapper, ListWrapper} from 'angular2/src/core/facade/collection'; +import {CONST, CONST_EXPR} from 'angular2/src/core/facade/lang'; import {BaseException, WrappedException} from 'angular2/src/core/facade/exceptions'; -import * as getTestabilityModule from './get_testability'; import {NgZone} from '../zone/ng_zone'; import {PromiseWrapper} from 'angular2/src/core/facade/async'; @@ -76,7 +76,7 @@ export class Testability { export class TestabilityRegistry { _applications: Map = new Map(); - constructor() { getTestabilityModule.GetTestability.addToWindow(this); } + constructor() { testabilityGetter.addToWindow(this); } registerApplication(token: any, testability: Testability) { this._applications.set(token, testability); @@ -99,3 +99,16 @@ export class TestabilityRegistry { return this.findTestabilityInTree(DOM.parentElement(elem)); } } + +export interface GetTestability { addToWindow(registry: TestabilityRegistry): void; } + +@CONST() +class NoopGetTestability implements GetTestability { + addToWindow(registry: TestabilityRegistry): void {} +} + +export function setTestabilityGetter(getter: GetTestability): void { + testabilityGetter = getter; +} + +var testabilityGetter: GetTestability = CONST_EXPR(new NoopGetTestability()); diff --git a/modules/angular2/src/web_workers/ui/impl.ts b/modules/angular2/src/web_workers/ui/impl.ts index af6fe2e21e..92b9effbbd 100644 --- a/modules/angular2/src/web_workers/ui/impl.ts +++ b/modules/angular2/src/web_workers/ui/impl.ts @@ -7,7 +7,7 @@ import {createInjector} from "./di_bindings"; import {MessageBus, MessageBusSink} from "angular2/src/web_workers/shared/message_bus"; -import {createNgZone} from 'angular2/src/core/application_common'; +import {createNgZone} from 'angular2/src/core/application_ref'; import {Injectable} from 'angular2/src/core/di'; import {BrowserDomAdapter} from 'angular2/src/core/dom/browser_adapter'; import {wtfInit} from 'angular2/src/core/profile/wtf_init'; diff --git a/modules/angular2/src/web_workers/worker/application.dart b/modules/angular2/src/web_workers/worker/application.dart index 48cf8ed211..f3309a4570 100644 --- a/modules/angular2/src/web_workers/worker/application.dart +++ b/modules/angular2/src/web_workers/worker/application.dart @@ -4,8 +4,8 @@ import "package:angular2/src/web_workers/shared/isolate_message_bus.dart"; import "package:angular2/src/web_workers/worker/application_common.dart" show bootstrapWebWorkerCommon; import "package:angular2/src/core/facade/async.dart" show Future; -import "package:angular2/src/core/application_ref.dart" show ApplicationRef; import "package:angular2/src/core/facade/lang.dart" show Type, BaseException; +import "package:angular2/src/core/compiler/dynamic_component_loader.dart" show ComponentRef; import "dart:isolate"; import "dart:async"; import 'dart:core'; @@ -21,7 +21,7 @@ import 'dart:core'; * bootstrap() in a regular Angular application * See the bootstrap() docs for more details. */ -Future bootstrapWebWorker( +Future bootstrapWebWorker( SendPort replyTo, Type appComponentType, [List componentInjectableBindings = null]) { ReceivePort rPort = new ReceivePort(); diff --git a/modules/angular2/src/web_workers/worker/application.ts b/modules/angular2/src/web_workers/worker/application.ts index e4764bd6a0..e69478563a 100644 --- a/modules/angular2/src/web_workers/worker/application.ts +++ b/modules/angular2/src/web_workers/worker/application.ts @@ -8,7 +8,7 @@ import {Binding, Injectable} from "angular2/src/core/di"; import {Map} from 'angular2/src/core/facade/collection'; import {Promise} from 'angular2/src/core/facade/async'; import {bootstrapWebWorkerCommon} from "angular2/src/web_workers/worker/application_common"; -import {ApplicationRef} from "angular2/src/core/application_ref"; +import {ComponentRef} from "angular2/src/core/compiler/dynamic_component_loader"; export * from "angular2/src/web_workers/shared/message_bus"; // TODO(jteplitz602) remove this and compile with lib.webworker.d.ts (#3492) @@ -28,7 +28,7 @@ var _postMessage: PostMessageInterface = postMessage; */ export function bootstrapWebWorker( appComponentType: Type, componentInjectableBindings: Array = null): - Promise { + Promise { var sink = new PostMessageBusSink({ postMessage: (message: any, transferrables?:[ArrayBuffer]) => { console.log("Sending", message); diff --git a/modules/angular2/src/web_workers/worker/application_common.ts b/modules/angular2/src/web_workers/worker/application_common.ts index 1357e5de84..12b64b71d5 100644 --- a/modules/angular2/src/web_workers/worker/application_common.ts +++ b/modules/angular2/src/web_workers/worker/application_common.ts @@ -1,5 +1,4 @@ import {Injector, bind, OpaqueToken, Binding} from 'angular2/src/core/di'; -import {DEFAULT_PIPES} from 'angular2/src/core/pipes'; import {FORM_BINDINGS} from 'angular2/src/core/forms'; import { NumberWrapper, @@ -10,50 +9,21 @@ import { print, stringify } from 'angular2/src/core/facade/lang'; -import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler'; -import {Reflector, reflector} from 'angular2/src/core/reflection/reflection'; -import { - Parser, - Lexer, - ChangeDetection, - DynamicChangeDetection, - JitChangeDetection, - PreGeneratedChangeDetection, - IterableDiffers, - defaultIterableDiffers, - KeyValueDiffers, - defaultKeyValueDiffers -} from 'angular2/src/core/change_detection/change_detection'; import {ExceptionHandler} from 'angular2/src/core/facade/exceptions'; -import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver'; -import {StyleUrlResolver} from 'angular2/src/core/render/dom/compiler/style_url_resolver'; -import {PipeResolver} from 'angular2/src/core/compiler/pipe_resolver'; -import {ViewResolver} from 'angular2/src/core/compiler/view_resolver'; -import {ListWrapper} from 'angular2/src/core/facade/collection'; import {Promise, PromiseWrapper, PromiseCompleter} from 'angular2/src/core/facade/async'; -import {NgZone} from 'angular2/src/core/zone/ng_zone'; -import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle'; import {XHR} from 'angular2/src/core/render/xhr'; import {WebWorkerXHRImpl} from 'angular2/src/web_workers/worker/xhr_impl'; -import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper'; -import {UrlResolver} from 'angular2/src/core/services/url_resolver'; import {AppRootUrl} from 'angular2/src/core/services/app_root_url'; -import { - ComponentRef, - DynamicComponentLoader -} from 'angular2/src/core/compiler/dynamic_component_loader'; -import {AppViewPool, APP_VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_pool'; -import {AppViewManager} from 'angular2/src/core/compiler/view_manager'; -import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils'; -import {AppViewListener} from 'angular2/src/core/compiler/view_listener'; -import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory'; import {WebWorkerRenderer, WebWorkerCompiler} from './renderer'; import {Renderer, RenderCompiler} from 'angular2/src/core/render/api'; -import {internalView} from 'angular2/src/core/compiler/view_ref'; import {ClientMessageBrokerFactory} from 'angular2/src/web_workers/shared/client_message_broker'; import {MessageBus} from 'angular2/src/web_workers/shared/message_bus'; -import {APP_COMPONENT_REF_PROMISE, APP_COMPONENT} from 'angular2/src/core/application_tokens'; -import {ApplicationRef} from 'angular2/src/core/application_ref'; +import { + platformCommon, + PlatformRef, + ApplicationRef, + applicationCommonBindings +} from 'angular2/src/core/application_ref'; import {Serializer} from "angular2/src/web_workers/shared/serializer"; import {ON_WEB_WORKER} from "angular2/src/web_workers/shared/api"; import {RenderProtoViewRefStore} from 'angular2/src/web_workers/shared/render_proto_view_ref_store'; @@ -63,11 +33,12 @@ import { import {ObservableWrapper} from 'angular2/src/core/facade/async'; import {SETUP_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api'; import {WebWorkerEventDispatcher} from 'angular2/src/web_workers/worker/event_dispatcher'; +import {ComponentRef} from 'angular2/src/core/compiler/dynamic_component_loader'; +import {NgZone} from 'angular2/src/core/zone/ng_zone'; -var _rootInjector: Injector; - -// Contains everything that is safe to share between applications. -var _rootBindings = [bind(Reflector).toValue(reflector)]; +export function platform(bindings?: Array): PlatformRef { + return platformCommon(bindings); +} class PrintLogger { log = print; @@ -76,29 +47,9 @@ class PrintLogger { logGroupEnd() {} } -function _injectorBindings(appComponentType, bus: MessageBus, initData: StringMap): +function webWorkerBindings(appComponentType, bus: MessageBus, initData: StringMap): Array { - var bestChangeDetection = new DynamicChangeDetection(); - if (PreGeneratedChangeDetection.isSupported()) { - bestChangeDetection = new PreGeneratedChangeDetection(); - } else if (JitChangeDetection.isSupported()) { - bestChangeDetection = new JitChangeDetection(); - } return [ - bind(APP_COMPONENT) - .toValue(appComponentType), - bind(APP_COMPONENT_REF_PROMISE) - .toFactory( - (dynamicComponentLoader, injector) => { - // TODO(rado): investigate whether to support bindings on root component. - return dynamicComponentLoader.loadAsRoot(appComponentType, null, injector) - .then((componentRef) => { return componentRef; }); - }, - [DynamicComponentLoader, Injector]), - - bind(appComponentType).toFactory((ref) => ref.instance, [APP_COMPONENT_REF_PROMISE]), - bind(LifeCycle).toFactory((exceptionHandler) => new LifeCycle(null, assertionsEnabled()), - [ExceptionHandler]), Serializer, bind(MessageBus).toValue(bus), ClientMessageBrokerFactory, @@ -109,93 +60,40 @@ function _injectorBindings(appComponentType, bus: MessageBus, initData: StringMa bind(ON_WEB_WORKER).toValue(true), RenderViewWithFragmentsStore, RenderProtoViewRefStore, - ProtoViewFactory, - AppViewPool, - bind(APP_VIEW_POOL_CAPACITY).toValue(10000), - AppViewManager, - AppViewManagerUtils, - AppViewListener, - Compiler, - CompilerCache, - ViewResolver, - DEFAULT_PIPES, - bind(IterableDiffers).toValue(defaultIterableDiffers), - bind(KeyValueDiffers).toValue(defaultKeyValueDiffers), - bind(ChangeDetection).toValue(bestChangeDetection), - DirectiveResolver, - UrlResolver, - StyleUrlResolver, - PipeResolver, - Parser, - Lexer, bind(ExceptionHandler).toFactory(() => new ExceptionHandler(new PrintLogger()), []), WebWorkerXHRImpl, bind(XHR).toAlias(WebWorkerXHRImpl), - ComponentUrlMapper, - DynamicComponentLoader, bind(AppRootUrl).toValue(new AppRootUrl(initData['rootUrl'])), WebWorkerEventDispatcher, FORM_BINDINGS ]; } -export function bootstrapWebWorkerCommon( - appComponentType: Type, bus: MessageBus, - componentInjectableBindings: Array = null): Promise { +export function bootstrapWebWorkerCommon(appComponentType: Type, bus: MessageBus, + appBindings: Array = null): + Promise { var bootstrapProcess: PromiseCompleter = PromiseWrapper.completer(); + var appPromise = platform().asyncApplication((zone: NgZone) => { + // TODO(rado): prepopulate template cache, so applications with only + // index.html and main.js are possible. + // + bus.attachToZone(zone); + bus.initChannel(SETUP_CHANNEL, false); - var zone = new NgZone({enableLongStackTrace: assertionsEnabled()}); - bus.attachToZone(zone); - - var subscription: any; - bus.initChannel(SETUP_CHANNEL, false); - var emitter = bus.from(SETUP_CHANNEL); - subscription = ObservableWrapper.subscribe(emitter, (message: StringMap) => { - zone.run(() => { - var exceptionHandler; - try { - var appInjector = - _createAppInjector(appComponentType, componentInjectableBindings, zone, bus, message); - exceptionHandler = appInjector.get(ExceptionHandler); - zone.overrideOnErrorHandler((e, s) => exceptionHandler.call(e, s)); - var compRefToken: Promise = appInjector.get(APP_COMPONENT_REF_PROMISE); - var tick = (componentRef) => { - var appChangeDetector = internalView(componentRef.hostView).changeDetector; - // retrieve life cycle: may have already been created if injected in root component - var lc = appInjector.get(LifeCycle); - lc.registerWith(zone, appChangeDetector); - lc.tick(); // the first tick that will bootstrap the app - bootstrapProcess.resolve(new ApplicationRef(componentRef, appComponentType, appInjector)); - }; - - var tickResult = PromiseWrapper.then(compRefToken, tick); - - PromiseWrapper.then(tickResult, - (_) => {}); // required for Dart to trigger the default error handler - PromiseWrapper.then(tickResult, null, - (err, stackTrace) => { bootstrapProcess.reject(err, stackTrace); }); - ObservableWrapper.dispose(subscription); - } catch (e) { - if (isPresent(exceptionHandler)) { - exceptionHandler.call(e, e.stack); - } - bootstrapProcess.reject(e, e.stack); + var subscription: any; + var emitter = bus.from(SETUP_CHANNEL); + subscription = ObservableWrapper.subscribe(emitter, (message: StringMap) => { + var bindings = + [applicationCommonBindings(), webWorkerBindings(appComponentType, bus, message)]; + if (isPresent(appBindings)) { + bindings.push(appBindings); } + bootstrapProcess.resolve(bindings); + ObservableWrapper.dispose(subscription); }); + + ObservableWrapper.callNext(bus.to(SETUP_CHANNEL), "ready"); + return bootstrapProcess.promise; }); - ObservableWrapper.callNext(bus.to(SETUP_CHANNEL), "ready"); - - return bootstrapProcess.promise; -} - -function _createAppInjector(appComponentType: Type, bindings: Array, - zone: NgZone, bus: MessageBus, initData: StringMap): - Injector { - if (isBlank(_rootInjector)) _rootInjector = Injector.resolveAndCreate(_rootBindings); - var mergedBindings: any[] = - isPresent(bindings) ? - ListWrapper.concat(_injectorBindings(appComponentType, bus, initData), bindings) : - _injectorBindings(appComponentType, bus, initData); - mergedBindings.push(bind(NgZone).toValue(zone)); - return _rootInjector.resolveAndCreateChild(mergedBindings); + return PromiseWrapper.then(appPromise, (app) => app.bootstrap(appComponentType)); } diff --git a/modules/angular2/test/core/application_spec.ts b/modules/angular2/test/core/application_spec.ts index 167dece1e3..a85ef7bb68 100644 --- a/modules/angular2/test/core/application_spec.ts +++ b/modules/angular2/test/core/application_spec.ts @@ -12,12 +12,12 @@ import { } from 'angular2/test_lib'; import {isPresent, stringify} from 'angular2/src/core/facade/lang'; import {bootstrap} from 'angular2/bootstrap'; -import {Component, Directive, View} from 'angular2/src/core/metadata'; +import {ApplicationRef} from 'angular2/src/core/application_ref'; +import {Component, Directive, View} from 'angular2/core'; import {DOM} from 'angular2/src/core/dom/dom_adapter'; import {DOCUMENT} from 'angular2/render'; import {PromiseWrapper} from 'angular2/src/core/facade/async'; import {bind, Inject, Injector, LifeCycle} from 'angular2/core'; -import {ApplicationRef} from 'angular2/src/core/application_ref'; import {ExceptionHandler} from 'angular2/src/core/facade/exceptions'; import {Testability, TestabilityRegistry} from 'angular2/src/core/testability/testability'; import {IS_DART} from '../platform';