feat(core): Add a long-form syntax for Angular bootstrapping.

This change adds a syntax for bootstrapping Angular on a page that allows more fine-grained control of the hierarchy created. platform() creates a platform injector (of which there can only be one). From the platform, .application() creates an Angular application including a Zone and all specified application bindings (e.g. for the DOM, HTTP, Compiler, Renderer, etc). At the application level, .bootstrap() will bootstrap the given component into that application.

Closes #3852
This commit is contained in:
Alex Rickabaugh
2015-09-02 15:19:26 -07:00
parent 193792c27f
commit 97d1844bfc
16 changed files with 503 additions and 408 deletions

View File

@ -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';

View File

@ -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<ApplicationRef> bootstrapWebWorker(
Future<ComponentRef> bootstrapWebWorker(
SendPort replyTo, Type appComponentType,
[List<dynamic> componentInjectableBindings = null]) {
ReceivePort rPort = new ReceivePort();

View File

@ -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 = <any>postMessage;
*/
export function bootstrapWebWorker(
appComponentType: Type, componentInjectableBindings: Array<Type | Binding | any[]> = null):
Promise<ApplicationRef> {
Promise<ComponentRef> {
var sink = new PostMessageBusSink({
postMessage: (message: any, transferrables?:[ArrayBuffer]) => {
console.log("Sending", message);

View File

@ -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<Type | Binding | any[]>): PlatformRef {
return platformCommon(bindings);
}
class PrintLogger {
log = print;
@ -76,29 +47,9 @@ class PrintLogger {
logGroupEnd() {}
}
function _injectorBindings(appComponentType, bus: MessageBus, initData: StringMap<string, any>):
function webWorkerBindings(appComponentType, bus: MessageBus, initData: StringMap<string, any>):
Array<Type | Binding | any[]> {
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<Type | Binding | any[]> = null): Promise<ApplicationRef> {
export function bootstrapWebWorkerCommon(appComponentType: Type, bus: MessageBus,
appBindings: Array<Type | Binding | any[]> = null):
Promise<ComponentRef> {
var bootstrapProcess: PromiseCompleter<any> = 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<string, any>) => {
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<any> = 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<string, any>) => {
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<Type | Binding | any[]>,
zone: NgZone, bus: MessageBus, initData: StringMap<string, any>):
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));
}