Skip to content

Commit 97d1844

Browse files
committed
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
1 parent 193792c commit 97d1844

16 files changed

+492
-397
lines changed

modules/angular2/core.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ export 'package:angular2/src/core/util.dart';
55
export 'package:angular2/src/core/di.dart';
66
export 'package:angular2/src/core/pipes.dart';
77
export 'package:angular2/src/core/facade.dart';
8-
export 'package:angular2/src/core/application_ref.dart';
98
// Do not export application for dart. Must import from angular2/bootstrap
109
//export 'package:angular2/src/core/application.dart';
10+
export 'package:angular2/src/core/application_ref.dart';
1111
export 'package:angular2/src/core/services.dart';
1212
export 'package:angular2/src/core/compiler.dart';
1313
export 'package:angular2/src/core/lifecycle.dart';

modules/angular2/src/core/application.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@ import 'package:angular2/src/core/reflection/reflection.dart' show reflector;
66
import 'package:angular2/src/core/reflection/reflection_capabilities.dart'
77
show ReflectionCapabilities;
88
import 'application_common.dart';
9-
import 'application_ref.dart';
109

11-
export 'application_ref.dart' show ApplicationRef;
10+
import 'package:angular2/src/core/compiler/dynamic_component_loader.dart';
11+
export 'package:angular2/src/core/compiler/dynamic_component_loader.dart' show ComponentRef;
1212

1313
/// Starts an application from a root component. This implementation uses
1414
/// mirrors. Angular 2 transformer automatically replaces this method with a
1515
/// static implementation (see `application_static.dart`) that does not use
1616
/// mirrors and produces a faster and more compact JS code.
1717
///
1818
/// See [commonBootstrap] for detailed documentation.
19-
Future<ApplicationRef> bootstrap(Type appComponentType,
19+
Future<ComponentRef> bootstrap(Type appComponentType,
2020
[List componentInjectableBindings]) {
2121
reflector.reflectionCapabilities = new ReflectionCapabilities();
2222
return commonBootstrap(appComponentType, componentInjectableBindings);
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
// Public API for Application
2-
export {ApplicationRef} from './application_ref';
32
export {APP_COMPONENT} from './application_tokens';
3+
export {platform, commonBootstrap as bootstrap} from './application_common';
4+
export {PlatformRef, ApplicationRef, rootBindings} from './application_ref';

modules/angular2/src/core/application_common.ts

Lines changed: 79 additions & 204 deletions
Large diffs are not rendered by default.
Lines changed: 297 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,320 @@
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+
4131
/**
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.
6156
*
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).
10160
*/
11-
export class ApplicationRef {
12-
_hostComponent: ComponentRef;
13-
_injector: Injector;
14-
_hostComponentType: Type;
161+
export class PlatformRef {
162+
/**
163+
* @private
164+
*/
165+
_applications: ApplicationRef[] = [];
15166

16167
/**
17168
* @private
18169
*/
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; }
24176

25177
/**
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.
27181
*/
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+
}
29186

30187
/**
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.
32192
*/
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+
34229

35230
/**
36-
* Dispose (un-load) the application.
231+
* Destroy the Angular platform and all Angular applications on the page.
37232
*/
38233
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);
41265
}
42266

43267
/**
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}.
45307
*/
46308
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+
}
47320
}

modules/angular2/src/core/application_static.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ library angular2.src.core.application_static;
22

33
import 'dart:async';
44
import 'application_common.dart';
5-
import 'application_ref.dart';
5+
import 'package:angular2/src/core/compiler/dynamic_component_loader.dart' show ComponentRef;
66

77
/// Starts an application from a root component.
88
///
99
/// See [commonBootstrap] for detailed documentation.
10-
Future<ApplicationRef> bootstrapStatic(Type appComponentType,
10+
Future<ComponentRef> bootstrapStatic(Type appComponentType,
1111
[List componentInjectableBindings, void initReflector()]) {
1212
if (initReflector != null) {
1313
initReflector();

0 commit comments

Comments
 (0)