refactor(core): fix `bootstrapModule` regarding zones and initializers (#10383)
This makes `bootstrapModuleFactory` wait for promises returned by `APP_INITIALIZER`s, also making `bootstrapModuleFactory` async. I.e. now `bootstrapModule` and `bootstrapModuleFactory` behave in the same way. This ensures that all code from module instantiation, to creating `ApplicationRef`s as well as calling `APP_INITIALIZERS` is run in the Angular zone. This also moves the invocation of the initializers from the `ApplicationRef` constructor into the `bootstrapModuleFactory` call, allowing initializers to get a hold of `ApplicationRef` (see #9101). Fixes #9101 Fixes #10363 Fixes #10205
This commit is contained in:
parent
633c7d1ebe
commit
a46437c57d
|
@ -13,8 +13,16 @@ import {serverPlatform} from '@angular/platform-server';
|
|||
import {MainModule} from '../src/module';
|
||||
import {MainModuleNgFactory} from '../src/module.ngfactory';
|
||||
|
||||
let mainModuleRef: NgModuleRef<MainModule> = null;
|
||||
beforeEach((done) => {
|
||||
serverPlatform().bootstrapModuleFactory(MainModuleNgFactory).then((moduleRef) => {
|
||||
mainModuleRef = moduleRef;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
export function createModule(): NgModuleRef<MainModule> {
|
||||
return serverPlatform().bootstrapModuleFactory(MainModuleNgFactory);
|
||||
return mainModuleRef;
|
||||
}
|
||||
|
||||
export function createComponent<C>(comp: {new (...args: any[]): C}): ComponentFixture<C> {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ObservableWrapper, PromiseWrapper} from '../src/facade/async';
|
||||
import {ObservableWrapper, PromiseCompleter, PromiseWrapper} from '../src/facade/async';
|
||||
import {ListWrapper} from '../src/facade/collection';
|
||||
import {BaseException, ExceptionHandler, unimplemented} from '../src/facade/exceptions';
|
||||
import {ConcreteType, IS_DART, Type, isBlank, isPresent, isPromise} from '../src/facade/lang';
|
||||
|
@ -27,7 +27,6 @@ import {NgZone, NgZoneError} from './zone/ng_zone';
|
|||
var _devMode: boolean = true;
|
||||
var _runModeLocked: boolean = false;
|
||||
var _platform: PlatformRef;
|
||||
var _inPlatformCreate: boolean = false;
|
||||
|
||||
/**
|
||||
* Disable Angular's development mode, which turns off assertions and other
|
||||
|
@ -78,19 +77,13 @@ export function isDevMode(): boolean {
|
|||
* @experimental APIs related to application bootstrap are currently under review.
|
||||
*/
|
||||
export function createPlatform(injector: Injector): PlatformRef {
|
||||
if (_inPlatformCreate) {
|
||||
throw new BaseException('Already creating a platform...');
|
||||
}
|
||||
if (isPresent(_platform) && !_platform.disposed) {
|
||||
throw new BaseException(
|
||||
'There can be only one platform. Destroy the previous one to create a new one.');
|
||||
}
|
||||
_inPlatformCreate = true;
|
||||
try {
|
||||
_platform = injector.get(PlatformRef);
|
||||
} finally {
|
||||
_inPlatformCreate = false;
|
||||
}
|
||||
_platform = injector.get(PlatformRef);
|
||||
const inits: Function[] = <Function[]>injector.get(PLATFORM_INITIALIZER, null);
|
||||
if (isPresent(inits)) inits.forEach(init => init());
|
||||
return _platform;
|
||||
}
|
||||
|
||||
|
@ -218,7 +211,7 @@ export abstract class PlatformRef {
|
|||
*
|
||||
* @experimental APIs related to application bootstrap are currently under review.
|
||||
*/
|
||||
bootstrapModuleFactory<M>(moduleFactory: NgModuleFactory<M>): NgModuleRef<M> {
|
||||
bootstrapModuleFactory<M>(moduleFactory: NgModuleFactory<M>): Promise<NgModuleRef<M>> {
|
||||
throw unimplemented();
|
||||
}
|
||||
|
||||
|
@ -244,8 +237,8 @@ export abstract class PlatformRef {
|
|||
}
|
||||
|
||||
/**
|
||||
*Register a listener to be called when the platform is disposed.
|
||||
*/
|
||||
* Register a listener to be called when the platform is disposed.
|
||||
*/
|
||||
abstract registerDisposeListener(dispose: () => void): void;
|
||||
|
||||
/**
|
||||
|
@ -262,6 +255,26 @@ export abstract class PlatformRef {
|
|||
get disposed(): boolean { throw unimplemented(); }
|
||||
}
|
||||
|
||||
function _callAndReportToExceptionHandler(
|
||||
exceptionHandler: ExceptionHandler, callback: () => any): any {
|
||||
try {
|
||||
const result = callback();
|
||||
if (isPromise(result)) {
|
||||
return result.catch((e: any) => {
|
||||
exceptionHandler.call(e);
|
||||
// rethrow as the exception handler might not do it
|
||||
throw e;
|
||||
});
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
} catch (e) {
|
||||
exceptionHandler.call(e);
|
||||
// rethrow as the exception handler might not do it
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class PlatformRef_ extends PlatformRef {
|
||||
/** @internal */
|
||||
|
@ -271,14 +284,7 @@ export class PlatformRef_ extends PlatformRef {
|
|||
|
||||
private _disposed: boolean = false;
|
||||
|
||||
constructor(private _injector: Injector) {
|
||||
super();
|
||||
if (!_inPlatformCreate) {
|
||||
throw new BaseException('Platforms have to be created via `createPlatform`!');
|
||||
}
|
||||
let inits: Function[] = <Function[]>_injector.get(PLATFORM_INITIALIZER, null);
|
||||
if (isPresent(inits)) inits.forEach(init => init());
|
||||
}
|
||||
constructor(private _injector: Injector) { super(); }
|
||||
|
||||
registerDisposeListener(dispose: () => void): void { this._disposeListeners.push(dispose); }
|
||||
|
||||
|
@ -286,8 +292,6 @@ export class PlatformRef_ extends PlatformRef {
|
|||
|
||||
get disposed() { return this._disposed; }
|
||||
|
||||
addApplication(appRef: ApplicationRef) { this._applications.push(appRef); }
|
||||
|
||||
dispose(): void {
|
||||
ListWrapper.clone(this._applications).forEach((app) => app.dispose());
|
||||
this._disposeListeners.forEach((dispose) => dispose());
|
||||
|
@ -297,15 +301,40 @@ export class PlatformRef_ extends PlatformRef {
|
|||
/** @internal */
|
||||
_applicationDisposed(app: ApplicationRef): void { ListWrapper.remove(this._applications, app); }
|
||||
|
||||
bootstrapModuleFactory<M>(moduleFactory: NgModuleFactory<M>): NgModuleRef<M> {
|
||||
bootstrapModuleFactory<M>(moduleFactory: NgModuleFactory<M>): Promise<NgModuleRef<M>> {
|
||||
// Note: We need to create the NgZone _before_ we instantiate the module,
|
||||
// as instantiating the module creates some providers eagerly.
|
||||
// So we create a mini parent injector that just contains the new NgZone and
|
||||
// pass that as parent to the NgModuleFactory.
|
||||
const ngZone = new NgZone({enableLongStackTrace: isDevMode()});
|
||||
const ngZoneInjector =
|
||||
ReflectiveInjector.resolveAndCreate([{provide: NgZone, useValue: ngZone}], this.injector);
|
||||
return ngZone.run(() => moduleFactory.create(ngZoneInjector));
|
||||
// Attention: Don't use ApplicationRef.run here,
|
||||
// as we want to be sure that all possible constructor calls are inside `ngZone.run`!
|
||||
return ngZone.run(() => {
|
||||
const ngZoneInjector =
|
||||
ReflectiveInjector.resolveAndCreate([{provide: NgZone, useValue: ngZone}], this.injector);
|
||||
const moduleRef = moduleFactory.create(ngZoneInjector);
|
||||
const exceptionHandler: ExceptionHandler = moduleRef.injector.get(ExceptionHandler);
|
||||
ObservableWrapper.subscribe(ngZone.onError, (error: NgZoneError) => {
|
||||
exceptionHandler.call(error.error, error.stackTrace);
|
||||
});
|
||||
return _callAndReportToExceptionHandler(exceptionHandler, () => {
|
||||
const appInits = moduleRef.injector.get(APP_INITIALIZER, null);
|
||||
const asyncInitPromises: Promise<any>[] = [];
|
||||
if (isPresent(appInits)) {
|
||||
for (let i = 0; i < appInits.length; i++) {
|
||||
const initResult = appInits[i]();
|
||||
if (isPromise(initResult)) {
|
||||
asyncInitPromises.push(initResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
const appRef: ApplicationRef_ = moduleRef.injector.get(ApplicationRef);
|
||||
return Promise.all(asyncInitPromises).then(() => {
|
||||
appRef.asyncInitDone();
|
||||
return moduleRef;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
bootstrapModule<M>(
|
||||
|
@ -315,11 +344,7 @@ export class PlatformRef_ extends PlatformRef {
|
|||
const compiler = compilerFactory.createCompiler(
|
||||
compilerOptions instanceof Array ? compilerOptions : [compilerOptions]);
|
||||
return compiler.compileModuleAsync(moduleType)
|
||||
.then((moduleFactory) => this.bootstrapModuleFactory(moduleFactory))
|
||||
.then((moduleRef) => {
|
||||
const appRef: ApplicationRef = moduleRef.injector.get(ApplicationRef);
|
||||
return appRef.waitForAsyncInitializers().then(() => moduleRef);
|
||||
});
|
||||
.then((moduleFactory) => this.bootstrapModuleFactory(moduleFactory));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -421,42 +446,17 @@ export class ApplicationRef_ extends ApplicationRef {
|
|||
/** @internal */
|
||||
private _enforceNoNewChanges: boolean = false;
|
||||
|
||||
private _asyncInitDonePromise: Promise<any>;
|
||||
private _asyncInitDone: boolean;
|
||||
/** @internal */
|
||||
_asyncInitDonePromise: PromiseCompleter<any> = PromiseWrapper.completer();
|
||||
|
||||
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[]) {
|
||||
@Optional() private _testability: Testability) {
|
||||
super();
|
||||
this._enforceNoNewChanges = isDevMode();
|
||||
this._asyncInitDonePromise = this.run(() => {
|
||||
var asyncInitResults: Promise<any>[] = [];
|
||||
var asyncInitDonePromise: Promise<any>;
|
||||
if (isPresent(inits)) {
|
||||
for (var i = 0; i < inits.length; i++) {
|
||||
var initResult = inits[i]();
|
||||
if (isPromise(initResult)) {
|
||||
asyncInitResults.push(initResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (asyncInitResults.length > 0) {
|
||||
asyncInitDonePromise =
|
||||
PromiseWrapper.all(asyncInitResults).then((_) => this._asyncInitDone = true);
|
||||
this._asyncInitDone = false;
|
||||
} else {
|
||||
this._asyncInitDone = true;
|
||||
asyncInitDonePromise = PromiseWrapper.resolve(true);
|
||||
}
|
||||
return asyncInitDonePromise;
|
||||
});
|
||||
ObservableWrapper.subscribe(this._zone.onError, (error: NgZoneError) => {
|
||||
this._exceptionHandler.call(error.error, error.stackTrace);
|
||||
});
|
||||
ObservableWrapper.subscribe(
|
||||
this._zone.onMicrotaskEmpty, (_) => { this._zone.run(() => { this.tick(); }); });
|
||||
}
|
||||
|
@ -475,40 +475,19 @@ export class ApplicationRef_ extends ApplicationRef {
|
|||
ListWrapper.remove(this._changeDetectorRefs, changeDetector);
|
||||
}
|
||||
|
||||
waitForAsyncInitializers(): Promise<any> { return this._asyncInitDonePromise; }
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
asyncInitDone() { this._asyncInitDonePromise.resolve(null); }
|
||||
|
||||
waitForAsyncInitializers(): Promise<any> { return this._asyncInitDonePromise.promise; }
|
||||
|
||||
run(callback: Function): any {
|
||||
var result: any;
|
||||
// Note: Don't use zone.runGuarded as we want to know about
|
||||
// the thrown exception!
|
||||
// Note: the completer needs to be created outside
|
||||
// of `zone.run` as Dart swallows rejected promises
|
||||
// via the onError callback of the promise.
|
||||
var completer = PromiseWrapper.completer();
|
||||
this._zone.run(() => {
|
||||
try {
|
||||
result = callback();
|
||||
if (isPromise(result)) {
|
||||
PromiseWrapper.then(
|
||||
result, (ref) => { completer.resolve(ref); },
|
||||
(err, stackTrace) => {
|
||||
completer.reject(err, stackTrace);
|
||||
this._exceptionHandler.call(err, stackTrace);
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
this._exceptionHandler.call(e, e.stack);
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
return isPromise(result) ? completer.promise : result;
|
||||
return this._zone.run(
|
||||
() => _callAndReportToExceptionHandler(this._exceptionHandler, <any>callback));
|
||||
}
|
||||
|
||||
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) {
|
||||
|
|
|
@ -6,62 +6,63 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AsyncTestCompleter, ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach, inject,} from '@angular/core/testing/testing_internal';
|
||||
import {SpyChangeDetectorRef} from './spies';
|
||||
import {ConcreteType} from '../src/facade/lang';
|
||||
import {ApplicationRef_, ApplicationRef} from '@angular/core/src/application_ref';
|
||||
import {Type, NgModule, CompilerFactory, Injector, APP_INITIALIZER, Component, ReflectiveInjector, PlatformRef, disposePlatform, createPlatformFactory, ComponentResolver, ComponentFactoryResolver, ChangeDetectorRef, ApplicationModule} from '@angular/core';
|
||||
import {platformCoreDynamic} from '@angular/compiler';
|
||||
import {APP_INITIALIZER, ChangeDetectorRef, CompilerFactory, Component, Injector, NgModule, PlatformRef} from '@angular/core';
|
||||
import {ApplicationRef, ApplicationRef_} from '@angular/core/src/application_ref';
|
||||
import {Console} from '@angular/core/src/console';
|
||||
import {BaseException} from '../src/facade/exceptions';
|
||||
import {PromiseWrapper, PromiseCompleter, TimerWrapper} from '../src/facade/async';
|
||||
import {ComponentFactory, ComponentRef_, ComponentRef} from '@angular/core/src/linker/component_factory';
|
||||
import {ComponentRef} from '@angular/core/src/linker/component_factory';
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
import {DOCUMENT} from '@angular/platform-browser/src/dom/dom_tokens';
|
||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||
|
||||
import {PromiseCompleter, PromiseWrapper} from '../src/facade/async';
|
||||
import {ExceptionHandler} from '../src/facade/exception_handler';
|
||||
import {BaseException} from '../src/facade/exceptions';
|
||||
import {ConcreteType} from '../src/facade/lang';
|
||||
import {TestBed, async, inject} from '../testing';
|
||||
|
||||
import {SpyChangeDetectorRef} from './spies';
|
||||
|
||||
@Component({selector: 'comp', template: 'hello'})
|
||||
class SomeComponent {
|
||||
}
|
||||
|
||||
export function main() {
|
||||
describe('bootstrap', () => {
|
||||
var defaultPlatform: PlatformRef;
|
||||
var errorLogger: _ArrayLogger;
|
||||
var someCompFactory: ComponentFactory<any>;
|
||||
var appProviders: any[];
|
||||
var fakeDoc: Document;
|
||||
|
||||
beforeEach(() => {
|
||||
fakeDoc = getDOM().createHtmlDocument();
|
||||
const el = getDOM().createElement('comp', fakeDoc);
|
||||
getDOM().appendChild(fakeDoc.body, el);
|
||||
errorLogger = new _ArrayLogger();
|
||||
disposePlatform();
|
||||
defaultPlatform = createPlatformFactory(platformCoreDynamic, 'test')();
|
||||
someCompFactory =
|
||||
new _MockComponentFactory(new _MockComponentRef(ReflectiveInjector.resolveAndCreate([])));
|
||||
appProviders = [
|
||||
{provide: Console, useValue: new _MockConsole()},
|
||||
{provide: ExceptionHandler, useValue: new ExceptionHandler(errorLogger, false)},
|
||||
{provide: ComponentResolver, useValue: new _MockComponentResolver(someCompFactory)}
|
||||
];
|
||||
});
|
||||
|
||||
afterEach(() => { disposePlatform(); });
|
||||
|
||||
function createModule(providers: any[] = []): ConcreteType<any> {
|
||||
@NgModule({providers: [appProviders, providers], imports: [ApplicationModule]})
|
||||
@NgModule({
|
||||
providers: [
|
||||
{provide: Console, useValue: new _MockConsole()},
|
||||
{provide: ExceptionHandler, useValue: new ExceptionHandler(errorLogger, false)},
|
||||
{provide: DOCUMENT, useValue: fakeDoc}, providers
|
||||
],
|
||||
imports: [BrowserModule],
|
||||
declarations: [SomeComponent],
|
||||
entryComponents: [SomeComponent]
|
||||
})
|
||||
class MyModule {
|
||||
}
|
||||
|
||||
return MyModule;
|
||||
}
|
||||
|
||||
function createApplication(
|
||||
providers: any[] = [], platform: PlatformRef = defaultPlatform): ApplicationRef_ {
|
||||
const compilerFactory: CompilerFactory = platform.injector.get(CompilerFactory);
|
||||
const compiler = compilerFactory.createCompiler();
|
||||
const appInjector =
|
||||
platform.bootstrapModuleFactory(compiler.compileModuleSync(createModule(providers)))
|
||||
.injector;
|
||||
return appInjector.get(ApplicationRef);
|
||||
}
|
||||
|
||||
describe('ApplicationRef', () => {
|
||||
var ref: ApplicationRef_;
|
||||
beforeEach(() => { TestBed.configureTestingModule({imports: [createModule()]}); });
|
||||
beforeEach(inject([ApplicationRef], (_ref: ApplicationRef_) => { ref = _ref; }));
|
||||
|
||||
it('should throw when reentering tick', () => {
|
||||
var cdRef = <any>new SpyChangeDetectorRef();
|
||||
var ref = createApplication();
|
||||
try {
|
||||
ref.registerChangeDetector(cdRef);
|
||||
cdRef.spy('detectChanges').andCallFake(() => ref.tick());
|
||||
|
@ -73,29 +74,42 @@ export function main() {
|
|||
|
||||
describe('run', () => {
|
||||
it('should rethrow errors even if the exceptionHandler is not rethrowing', () => {
|
||||
var ref = createApplication();
|
||||
expect(() => ref.run(() => { throw new BaseException('Test'); })).toThrowError('Test');
|
||||
});
|
||||
|
||||
it('should return a promise with rejected errors even if the exceptionHandler is not rethrowing',
|
||||
inject(
|
||||
[AsyncTestCompleter, Injector], (async: AsyncTestCompleter, injector: Injector) => {
|
||||
var ref = createApplication();
|
||||
var promise = ref.run(() => PromiseWrapper.reject('Test', null));
|
||||
PromiseWrapper.catchError(promise, (e) => {
|
||||
expect(e).toEqual('Test');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
async(() => {
|
||||
var promise: Promise<any> = ref.run(() => Promise.reject('Test'));
|
||||
promise.then(() => expect(false).toBe(true), (e) => { expect(e).toEqual('Test'); });
|
||||
}));
|
||||
});
|
||||
|
||||
describe('registerBootstrapListener', () => {
|
||||
it('should be called when a component is bootstrapped', () => {
|
||||
const capturedCompRefs: ComponentRef<any>[] = [];
|
||||
ref.registerBootstrapListener((compRef) => capturedCompRefs.push(compRef));
|
||||
const compRef = ref.bootstrap(SomeComponent);
|
||||
expect(capturedCompRefs).toEqual([compRef]);
|
||||
});
|
||||
|
||||
it('should be called immediately when a component was bootstrapped before', () => {
|
||||
ref.registerBootstrapListener((compRef) => capturedCompRefs.push(compRef));
|
||||
const capturedCompRefs: ComponentRef<any>[] = [];
|
||||
const compRef = ref.bootstrap(SomeComponent);
|
||||
expect(capturedCompRefs).toEqual([compRef]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('bootstrapModule', () => {
|
||||
it('should wait for asynchronous app initializers',
|
||||
inject([AsyncTestCompleter, Injector], (async: AsyncTestCompleter, injector: Injector) => {
|
||||
var defaultPlatform: PlatformRef;
|
||||
beforeEach(
|
||||
inject([PlatformRef], (_platform: PlatformRef) => { defaultPlatform = _platform; }));
|
||||
|
||||
it('should wait for asynchronous app initializers', async(() => {
|
||||
let completer: PromiseCompleter<any> = PromiseWrapper.completer();
|
||||
var initializerDone = false;
|
||||
TimerWrapper.setTimeout(() => {
|
||||
setTimeout(() => {
|
||||
completer.resolve(true);
|
||||
initializerDone = true;
|
||||
}, 1);
|
||||
|
@ -103,24 +117,73 @@ export function main() {
|
|||
defaultPlatform
|
||||
.bootstrapModule(createModule(
|
||||
[{provide: APP_INITIALIZER, useValue: () => completer.promise, multi: true}]))
|
||||
.then(_ => {
|
||||
expect(initializerDone).toBe(true);
|
||||
async.done();
|
||||
.then(_ => { expect(initializerDone).toBe(true); });
|
||||
}));
|
||||
|
||||
it('should rethrow sync errors even if the exceptionHandler is not rethrowing', async(() => {
|
||||
defaultPlatform
|
||||
.bootstrapModule(createModule(
|
||||
[{provide: APP_INITIALIZER, useValue: () => { throw 'Test'; }, multi: true}]))
|
||||
.then(() => expect(false).toBe(true), (e) => {
|
||||
expect(e).toBe('Test');
|
||||
expect(errorLogger.res).toEqual(['EXCEPTION: Test']);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should rethrow promise errors even if the exceptionHandler is not rethrowing',
|
||||
async(() => {
|
||||
defaultPlatform
|
||||
.bootstrapModule(createModule([
|
||||
{provide: APP_INITIALIZER, useValue: () => Promise.reject('Test'), multi: true}
|
||||
]))
|
||||
.then(() => expect(false).toBe(true), (e) => {
|
||||
expect(e).toBe('Test');
|
||||
expect(errorLogger.res).toEqual(['EXCEPTION: Test']);
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('ApplicationRef.bootstrap', () => {
|
||||
it('should throw if an APP_INITIIALIZER is not yet resolved',
|
||||
inject([Injector], (injector: Injector) => {
|
||||
var app = createApplication([{
|
||||
provide: APP_INITIALIZER,
|
||||
useValue: () => PromiseWrapper.completer().promise,
|
||||
multi: true
|
||||
}]);
|
||||
expect(() => app.bootstrap(someCompFactory))
|
||||
.toThrowError(
|
||||
'Cannot bootstrap as there are still asynchronous initializers running. Wait for them using waitForAsyncInitializers().');
|
||||
describe('bootstrapModuleFactory', () => {
|
||||
var defaultPlatform: PlatformRef;
|
||||
beforeEach(
|
||||
inject([PlatformRef], (_platform: PlatformRef) => { defaultPlatform = _platform; }));
|
||||
it('should wait for asynchronous app initializers', async(() => {
|
||||
let completer: PromiseCompleter<any> = PromiseWrapper.completer();
|
||||
var initializerDone = false;
|
||||
setTimeout(() => {
|
||||
completer.resolve(true);
|
||||
initializerDone = true;
|
||||
}, 1);
|
||||
|
||||
const compilerFactory: CompilerFactory =
|
||||
defaultPlatform.injector.get(CompilerFactory, null);
|
||||
const moduleFactory = compilerFactory.createCompiler().compileModuleSync(createModule(
|
||||
[{provide: APP_INITIALIZER, useValue: () => completer.promise, multi: true}]));
|
||||
defaultPlatform.bootstrapModuleFactory(moduleFactory).then(_ => {
|
||||
expect(initializerDone).toBe(true);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should rethrow sync errors even if the exceptionHandler is not rethrowing', async(() => {
|
||||
const compilerFactory: CompilerFactory =
|
||||
defaultPlatform.injector.get(CompilerFactory, null);
|
||||
const moduleFactory = compilerFactory.createCompiler().compileModuleSync(createModule(
|
||||
[{provide: APP_INITIALIZER, useValue: () => { throw 'Test'; }, multi: true}]));
|
||||
expect(() => defaultPlatform.bootstrapModuleFactory(moduleFactory)).toThrow('Test');
|
||||
expect(errorLogger.res).toEqual(['EXCEPTION: Test']);
|
||||
}));
|
||||
|
||||
it('should rethrow promise errors even if the exceptionHandler is not rethrowing',
|
||||
async(() => {
|
||||
const compilerFactory: CompilerFactory =
|
||||
defaultPlatform.injector.get(CompilerFactory, null);
|
||||
const moduleFactory = compilerFactory.createCompiler().compileModuleSync(createModule(
|
||||
[{provide: APP_INITIALIZER, useValue: () => Promise.reject('Test'), multi: true}]));
|
||||
defaultPlatform.bootstrapModuleFactory(moduleFactory)
|
||||
.then(() => expect(false).toBe(true), (e) => {
|
||||
expect(e).toBe('Test');
|
||||
expect(errorLogger.res).toEqual(['EXCEPTION: Test']);
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
@ -138,31 +201,6 @@ class _ArrayLogger {
|
|||
logGroupEnd(){};
|
||||
}
|
||||
|
||||
class _MockComponentFactory extends ComponentFactory<any> {
|
||||
constructor(private _compRef: ComponentRef<any>) { super(null, null, null); }
|
||||
create(
|
||||
injector: Injector, projectableNodes: any[][] = null,
|
||||
rootSelectorOrNode: string|any = null): ComponentRef<any> {
|
||||
return this._compRef;
|
||||
}
|
||||
}
|
||||
|
||||
class _MockComponentResolver implements ComponentResolver {
|
||||
constructor(private _compFactory: ComponentFactory<any>) {}
|
||||
|
||||
resolveComponent(type: Type): Promise<ComponentFactory<any>> {
|
||||
return PromiseWrapper.resolve(this._compFactory);
|
||||
}
|
||||
clearCache() {}
|
||||
}
|
||||
|
||||
class _MockComponentRef extends ComponentRef_<any> {
|
||||
constructor(private _injector: Injector) { super(null, null); }
|
||||
get injector(): Injector { return this._injector; }
|
||||
get changeDetectorRef(): ChangeDetectorRef { return <any>new SpyChangeDetectorRef(); }
|
||||
onDestroy(cb: Function) {}
|
||||
}
|
||||
|
||||
class _MockConsole implements Console {
|
||||
log(message: string) {}
|
||||
warn(message: string) {}
|
||||
|
|
|
@ -54,19 +54,10 @@ export function rootRoute(router: Router): ActivatedRoute {
|
|||
return router.routerState.root;
|
||||
}
|
||||
|
||||
export function setupRouterInitializer(injector: Injector) {
|
||||
// https://github.com/angular/angular/issues/9101
|
||||
// Delay the router instantiation to avoid circular dependency (ApplicationRef ->
|
||||
// APP_INITIALIZER -> Router)
|
||||
setTimeout(() => {
|
||||
const appRef = injector.get(ApplicationRef);
|
||||
if (appRef.componentTypes.length == 0) {
|
||||
appRef.registerBootstrapListener(() => { injector.get(Router).initialNavigation(); });
|
||||
} else {
|
||||
injector.get(Router).initialNavigation();
|
||||
}
|
||||
}, 0);
|
||||
return (): any => null;
|
||||
export function setupRouterInitializer(injector: Injector, appRef: ApplicationRef) {
|
||||
return () => {
|
||||
appRef.registerBootstrapListener(() => { injector.get(Router).initialNavigation(); });
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -76,18 +76,11 @@ export const ROUTER_PROVIDERS: any[] = [
|
|||
*/
|
||||
@NgModule({declarations: ROUTER_DIRECTIVES, exports: ROUTER_DIRECTIVES})
|
||||
export class RouterModule {
|
||||
constructor(private injector: Injector) {
|
||||
constructor(private injector: Injector, appRef: ApplicationRef) {
|
||||
// do the initialization only once
|
||||
if ((<any>injector).parent.get(RouterModule, null)) return;
|
||||
|
||||
setTimeout(() => {
|
||||
const appRef = injector.get(ApplicationRef);
|
||||
if (appRef.componentTypes.length == 0) {
|
||||
appRef.registerBootstrapListener(() => { injector.get(Router).initialNavigation(); });
|
||||
} else {
|
||||
injector.get(Router).initialNavigation();
|
||||
}
|
||||
}, 0);
|
||||
appRef.registerBootstrapListener(() => { injector.get(Router).initialNavigation(); });
|
||||
}
|
||||
|
||||
static forRoot(routes: Routes, config?: ExtraOptions): ModuleWithProviders {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ApplicationRef, Compiler, CompilerFactory, ComponentFactory, ComponentResolver, Injector, NgModule, NgZone, PlatformRef, Provider, ReflectiveInjector, Testability, Type, provide} from '@angular/core';
|
||||
import {ApplicationRef, Compiler, CompilerFactory, ComponentFactory, ComponentResolver, Injector, NgModule, NgModuleRef, NgZone, PlatformRef, Provider, ReflectiveInjector, Testability, Type, provide} from '@angular/core';
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
||||
|
||||
|
@ -288,10 +288,17 @@ export class UpgradeAdapter {
|
|||
class DynamicModule {
|
||||
}
|
||||
|
||||
const compilerFactory: CompilerFactory = platformRef.injector.get(CompilerFactory);
|
||||
var moduleRef = platformRef.bootstrapModuleFactory(
|
||||
compilerFactory.createCompiler().compileModuleSync(DynamicModule));
|
||||
platformRef.bootstrapModule(DynamicModule).then((moduleRef) => {
|
||||
ng1Injector = this._afterNg2ModuleBootstrap(moduleRef, upgrade, element, modules, config);
|
||||
});
|
||||
return upgrade;
|
||||
}
|
||||
|
||||
private _afterNg2ModuleBootstrap(
|
||||
moduleRef: NgModuleRef<any>, upgrade: UpgradeAdapterRef, element: Element, modules?: any[],
|
||||
config?: angular.IAngularBootstrapConfig): angular.IInjectorService {
|
||||
const boundCompiler: Compiler = moduleRef.injector.get(Compiler);
|
||||
var ng1Injector: angular.IInjectorService = null;
|
||||
var applicationRef: ApplicationRef = moduleRef.injector.get(ApplicationRef);
|
||||
var injector: Injector = applicationRef.injector;
|
||||
var ngZone: NgZone = injector.get(NgZone);
|
||||
|
@ -398,7 +405,7 @@ export class UpgradeAdapter {
|
|||
}
|
||||
});
|
||||
}, onError);
|
||||
return upgrade;
|
||||
return ng1Injector;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,10 +16,6 @@ import {HashLocationStrategy, LocationStrategy} from '@angular/common';
|
|||
|
||||
@Component({selector: 'app', templateUrl: 'app.html'})
|
||||
export class App {
|
||||
constructor(router: Router) {
|
||||
// this should not be required once web worker bootstrap method can use modules
|
||||
router.initialNavigation();
|
||||
}
|
||||
}
|
||||
|
||||
export const ROUTES = [
|
||||
|
|
|
@ -979,7 +979,7 @@ export declare abstract class PlatformRef {
|
|||
disposed: boolean;
|
||||
injector: Injector;
|
||||
/** @stable */ bootstrapModule<M>(moduleType: ConcreteType<M>, compilerOptions?: CompilerOptions | CompilerOptions[]): Promise<NgModuleRef<M>>;
|
||||
/** @experimental */ bootstrapModuleFactory<M>(moduleFactory: NgModuleFactory<M>): NgModuleRef<M>;
|
||||
/** @experimental */ bootstrapModuleFactory<M>(moduleFactory: NgModuleFactory<M>): Promise<NgModuleRef<M>>;
|
||||
abstract dispose(): void;
|
||||
abstract registerDisposeListener(dispose: () => void): void;
|
||||
}
|
||||
|
|
|
@ -222,7 +222,7 @@ export declare class RouterLinkWithHref implements OnChanges, OnDestroy {
|
|||
|
||||
/** @experimental */
|
||||
export declare class RouterModule {
|
||||
constructor(injector: Injector);
|
||||
constructor(injector: Injector, appRef: ApplicationRef);
|
||||
static forChild(routes: Routes): ModuleWithProviders;
|
||||
static forRoot(routes: Routes, config?: ExtraOptions): ModuleWithProviders;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue