feat(browser): use AppModules for bootstrap in the browser

This introduces the `BrowserModule` to be used for long form
bootstrap and offline compile bootstrap:

```
@AppModule({
  modules: [BrowserModule],
  precompile: [MainComponent],
  providers: […], // additional providers
  directives: […], // additional platform directives
  pipes: […] // additional platform pipes
})
class MyModule {
  constructor(appRef: ApplicationRef) {
    appRef.bootstrap(MainComponent);
  }
}

// offline compile
import {bootstrapModuleFactory} from ‘@angular/platform-browser’;
bootstrapModuleFactory(MyModuleNgFactory);

// runtime compile long form
import {bootstrapModule} from ‘@angular/platform-browser-dynamic’;
bootstrapModule(MyModule);
```

The short form, `bootstrap(...)`, can now creates a module on the fly,
given `directives`, `pipes, `providers`, `precompile` and `modules`
properties.

Related changes:
- make `SanitizationService`, `SecurityContext` public in `@angular/core` so that the offline compiler can resolve the token
- move `AnimationDriver` to `platform-browser` and make it
  public so that the offline compiler can resolve the token

BREAKING CHANGES:
- short form bootstrap does no longer allow
  to inject compiler internals (i.e. everything 
  from `@angular/compiler). Inject `Compiler` instead.
  To provide custom providers for the compiler,
  create a custom compiler via `browserCompiler({providers: [...]})`
  and pass that into the `bootstrap` method.
This commit is contained in:
Tobias Bosch
2016-06-30 13:07:17 -07:00
parent 74b45dfbf8
commit 3f55aa609f
71 changed files with 793 additions and 406 deletions

View File

@ -9,23 +9,35 @@
import {ObservableWrapper, PromiseWrapper} from '../src/facade/async';
import {ListWrapper} from '../src/facade/collection';
import {BaseException, ExceptionHandler, unimplemented} from '../src/facade/exceptions';
import {IS_DART, Type, isBlank, isPresent, isPromise} from '../src/facade/lang';
import {ConcreteType, IS_DART, Type, isBlank, isPresent, isPromise} from '../src/facade/lang';
import {APP_INITIALIZER, PLATFORM_INITIALIZER} from './application_tokens';
import {ChangeDetectorRef} from './change_detection/change_detector_ref';
import {Console} from './console';
import {Injectable, Injector} from './di';
import {Inject, Injectable, Injector, Optional, OptionalMetadata, SkipSelf, SkipSelfMetadata, forwardRef} from './di';
import {ComponentFactory, ComponentRef} from './linker/component_factory';
import {ComponentFactoryResolver} from './linker/component_factory_resolver';
import {ComponentResolver} from './linker/component_resolver';
import {WtfScopeFn, wtfCreateScope, wtfLeave} from './profile/profile';
import {Testability, TestabilityRegistry} from './testability/testability';
import {NgZone, NgZoneError} from './zone/ng_zone';
/**
* Create an Angular zone.
* @experimental
*/
export function createNgZone(): NgZone {
export function createNgZone(parent: NgZone): NgZone {
// If an NgZone is already present in the parent injector,
// use that one. Creating the NgZone in the same injector as the
// application is dangerous as some services might get created before
// the NgZone has been created.
// We keep the NgZone factory in the application providers for
// backwards compatibility for now though.
if (parent) {
return parent;
}
return new NgZone({enableLongStackTrace: isDevMode()});
}
@ -279,7 +291,7 @@ export abstract class ApplicationRef {
* ### Example
* {@example core/ts/platform/platform.ts region='longform'}
*/
abstract bootstrap<C>(componentFactory: ComponentFactory<C>): ComponentRef<C>;
abstract bootstrap<C>(componentFactory: ComponentFactory<C>|ConcreteType<C>): ComponentRef<C>;
/**
* Retrieve the application {@link Injector}.
@ -334,18 +346,19 @@ export class ApplicationRef_ extends ApplicationRef {
/** @internal */
private _enforceNoNewChanges: boolean = false;
private _exceptionHandler: ExceptionHandler;
private _asyncInitDonePromise: Promise<any>;
private _asyncInitDone: boolean;
constructor(private _platform: PlatformRef_, private _zone: NgZone, private _injector: Injector) {
constructor(
private _platform: PlatformRef_, private _zone: NgZone, private _console: Console,
private _injector: Injector, private _exceptionHandler: ExceptionHandler,
private _componentFactoryResolver: ComponentFactoryResolver,
@Optional() private _testabilityRegistry: TestabilityRegistry,
@Optional() private _testability: Testability,
@Optional() @Inject(APP_INITIALIZER) inits: Function[]) {
super();
var zone: NgZone = _injector.get(NgZone);
this._enforceNoNewChanges = isDevMode();
zone.run(() => { this._exceptionHandler = _injector.get(ExceptionHandler); });
this._asyncInitDonePromise = this.run(() => {
let inits: Function[] = _injector.get(APP_INITIALIZER, null);
var asyncInitResults: Promise<any>[] = [];
var asyncInitDonePromise: Promise<any>;
if (isPresent(inits)) {
@ -366,7 +379,7 @@ export class ApplicationRef_ extends ApplicationRef {
}
return asyncInitDonePromise;
});
ObservableWrapper.subscribe(zone.onError, (error: NgZoneError) => {
ObservableWrapper.subscribe(this._zone.onError, (error: NgZoneError) => {
this._exceptionHandler.call(error.error, error.stackTrace);
});
ObservableWrapper.subscribe(
@ -390,7 +403,6 @@ export class ApplicationRef_ extends ApplicationRef {
waitForAsyncInitializers(): Promise<any> { return this._asyncInitDonePromise; }
run(callback: Function): any {
var zone = this.injector.get(NgZone);
var result: any;
// Note: Don't use zone.runGuarded as we want to know about
// the thrown exception!
@ -398,7 +410,7 @@ export class ApplicationRef_ extends ApplicationRef {
// of `zone.run` as Dart swallows rejected promises
// via the onError callback of the promise.
var completer = PromiseWrapper.completer();
zone.run(() => {
this._zone.run(() => {
try {
result = callback();
if (isPromise(result)) {
@ -417,12 +429,19 @@ export class ApplicationRef_ extends ApplicationRef {
return isPromise(result) ? completer.promise : result;
}
bootstrap<C>(componentFactory: ComponentFactory<C>): ComponentRef<C> {
bootstrap<C>(componentOrFactory: ComponentFactory<C>|ConcreteType<C>): ComponentRef<C> {
if (!this._asyncInitDone) {
throw new BaseException(
'Cannot bootstrap as there are still asynchronous initializers running. Wait for them using waitForAsyncInitializers().');
}
return this.run(() => {
let componentFactory: ComponentFactory<C>;
if (componentOrFactory instanceof ComponentFactory) {
componentFactory = componentOrFactory;
} else {
componentFactory =
this._componentFactoryResolver.resolveComponentFactory(componentOrFactory);
}
this._rootComponentTypes.push(componentFactory.componentType);
var compRef = componentFactory.create(this._injector, [], componentFactory.selector);
compRef.onDestroy(() => { this._unloadComponent(compRef); });
@ -433,11 +452,10 @@ export class ApplicationRef_ extends ApplicationRef {
}
this._loadComponent(compRef);
let c: Console = this._injector.get(Console);
if (isDevMode()) {
let prodDescription = IS_DART ? 'Production mode is disabled in Dart.' :
'Call enableProdMode() to enable the production mode.';
c.log(`Angular 2 is running in the development mode. ${prodDescription}`);
this._console.log(`Angular 2 is running in the development mode. ${prodDescription}`);
}
return compRef;
});
@ -500,7 +518,11 @@ export const PLATFORM_CORE_PROVIDERS =
];
export const APPLICATION_CORE_PROVIDERS = /*@ts2dart_const*/[
/* @ts2dart_Provider */ {provide: NgZone, useFactory: createNgZone, deps: [] as any},
/* @ts2dart_Provider */ {
provide: NgZone,
useFactory: createNgZone,
deps: <any>[[new SkipSelfMetadata(), new OptionalMetadata(), NgZone]]
},
ApplicationRef_,
/* @ts2dart_Provider */ {provide: ApplicationRef, useExisting: ApplicationRef_},
];