feat(core/application_ref): Allow asyncronous app initializers.
closes #5929. Closes #6063
This commit is contained in:
parent
f7424d5aeb
commit
df3074fdfe
|
@ -217,25 +217,36 @@ export class PlatformRef_ extends PlatformRef {
|
|||
|
||||
application(providers: Array<Type | Provider | any[]>): ApplicationRef {
|
||||
var app = this._initApp(createNgZone(), providers);
|
||||
return app;
|
||||
if (PromiseWrapper.isPromise(app)) {
|
||||
throw new BaseException(
|
||||
"Cannot use asyncronous app initializers with application. Use asyncApplication instead.");
|
||||
}
|
||||
return <ApplicationRef>app;
|
||||
}
|
||||
|
||||
asyncApplication(bindingFn: (zone: NgZone) => Promise<Array<Type | Provider | any[]>>,
|
||||
additionalProviders?: Array<Type | Provider | any[]>): Promise<ApplicationRef> {
|
||||
var zone = createNgZone();
|
||||
var completer = PromiseWrapper.completer();
|
||||
zone.run(() => {
|
||||
PromiseWrapper.then(bindingFn(zone), (providers: Array<Type | Provider | any[]>) => {
|
||||
if (isPresent(additionalProviders)) {
|
||||
providers = ListWrapper.concat(providers, additionalProviders);
|
||||
}
|
||||
completer.resolve(this._initApp(zone, providers));
|
||||
if (bindingFn === null) {
|
||||
completer.resolve(this._initApp(zone, additionalProviders));
|
||||
} else {
|
||||
zone.run(() => {
|
||||
PromiseWrapper.then(bindingFn(zone), (providers: Array<Type | Provider | any[]>) => {
|
||||
if (isPresent(additionalProviders)) {
|
||||
providers = ListWrapper.concat(providers, additionalProviders);
|
||||
}
|
||||
let promise = this._initApp(zone, providers);
|
||||
completer.resolve(promise);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
return completer.promise;
|
||||
}
|
||||
|
||||
private _initApp(zone: NgZone, providers: Array<Type | Provider | any[]>): ApplicationRef {
|
||||
private _initApp(zone: NgZone,
|
||||
providers: Array<Type | Provider | any[]>): Promise<ApplicationRef>|
|
||||
ApplicationRef {
|
||||
var injector: Injector;
|
||||
var app: ApplicationRef;
|
||||
zone.run(() => {
|
||||
|
@ -259,8 +270,12 @@ export class PlatformRef_ extends PlatformRef {
|
|||
});
|
||||
app = new ApplicationRef_(this, zone, injector);
|
||||
this._applications.push(app);
|
||||
_runAppInitializers(injector);
|
||||
return app;
|
||||
var promise = _runAppInitializers(injector);
|
||||
if (promise !== null) {
|
||||
return PromiseWrapper.then(promise, (_) => app);
|
||||
} else {
|
||||
return app;
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
|
@ -273,9 +288,22 @@ export class PlatformRef_ extends PlatformRef {
|
|||
_applicationDisposed(app: ApplicationRef): void { ListWrapper.remove(this._applications, app); }
|
||||
}
|
||||
|
||||
function _runAppInitializers(injector: Injector): void {
|
||||
function _runAppInitializers(injector: Injector): Promise<any> {
|
||||
let inits: Function[] = injector.getOptional(APP_INITIALIZER);
|
||||
if (isPresent(inits)) inits.forEach(init => init());
|
||||
let promises: Promise<any>[] = [];
|
||||
if (isPresent(inits)) {
|
||||
inits.forEach(init => {
|
||||
var retVal = init();
|
||||
if (PromiseWrapper.isPromise(retVal)) {
|
||||
promises.push(retVal);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (promises.length > 0) {
|
||||
return PromiseWrapper.all(promises);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -11,13 +11,14 @@ import {
|
|||
AsyncTestCompleter,
|
||||
fakeAsync,
|
||||
tick,
|
||||
inject
|
||||
inject,
|
||||
SpyObject
|
||||
} from 'angular2/testing_internal';
|
||||
import {SpyChangeDetector} from './spies';
|
||||
import {ApplicationRef_, PlatformRef_} from "angular2/src/core/application_ref";
|
||||
import {Injector, Provider} from "angular2/core";
|
||||
import {ApplicationRef_, ApplicationRef, PlatformRef_} from "angular2/src/core/application_ref";
|
||||
import {Injector, Provider, APP_INITIALIZER} from "angular2/core";
|
||||
import {ChangeDetectorRef_} from "angular2/src/core/change_detection/change_detector_ref";
|
||||
import {PromiseWrapper} from "angular2/src/facade/async";
|
||||
import {PromiseWrapper, PromiseCompleter, TimerWrapper} from "angular2/src/facade/async";
|
||||
import {ListWrapper} from "angular2/src/facade/collection";
|
||||
|
||||
export function main() {
|
||||
|
@ -33,7 +34,14 @@ export function main() {
|
|||
|
||||
describe("PlatformRef", () => {
|
||||
describe("asyncApplication", () => {
|
||||
it("should merge synchronous and asynchronous providers",
|
||||
function expectProviders(injector: Injector, providers: Array<any>): void {
|
||||
for (let i = 0; i < providers.length; i++) {
|
||||
let provider = providers[i];
|
||||
expect(injector.get(provider.token)).toBe(provider.useValue);
|
||||
}
|
||||
}
|
||||
|
||||
it("should merge syncronous and asyncronous providers",
|
||||
inject([AsyncTestCompleter, Injector], (async, injector) => {
|
||||
let ref = new PlatformRef_(injector, null);
|
||||
let ASYNC_PROVIDERS = [new Provider(Foo, {useValue: new Foo()})];
|
||||
|
@ -41,13 +49,98 @@ export function main() {
|
|||
ref.asyncApplication((zone) => PromiseWrapper.resolve(ASYNC_PROVIDERS), SYNC_PROVIDERS)
|
||||
.then((appRef) => {
|
||||
var providers = ListWrapper.concat(ASYNC_PROVIDERS, SYNC_PROVIDERS);
|
||||
for (var i = 0; i < providers.length; i++) {
|
||||
var provider = providers[i];
|
||||
expect(appRef.injector.get(provider.token)).toBe(provider.useValue);
|
||||
}
|
||||
expectProviders(appRef.injector, providers);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it("should allow function to be null",
|
||||
inject([AsyncTestCompleter, Injector], (async, injector) => {
|
||||
let ref = new PlatformRef_(injector, null);
|
||||
let SYNC_PROVIDERS = [new Provider(Bar, {useValue: new Bar()})];
|
||||
ref.asyncApplication(null, SYNC_PROVIDERS)
|
||||
.then((appRef) => {
|
||||
expectProviders(appRef.injector, SYNC_PROVIDERS);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
function mockAsyncAppInitializer(completer, providers: Array<any> = null,
|
||||
injector?: Injector) {
|
||||
return () => {
|
||||
if (providers != null) {
|
||||
expectProviders(injector, providers);
|
||||
}
|
||||
TimerWrapper.setTimeout(() => completer.resolve(true), 1);
|
||||
return completer.promise;
|
||||
};
|
||||
}
|
||||
|
||||
function createSpyPromiseCompleter(): SpyObject {
|
||||
let completer = PromiseWrapper.completer();
|
||||
let completerSpy = <any>new SpyObject();
|
||||
// Note that in TypeScript we need to provide a value for the promise attribute
|
||||
// whereas in dart we need to override the promise getter
|
||||
completerSpy.promise = completer.promise;
|
||||
completerSpy.spy("get:promise").andReturn(completer.promise);
|
||||
completerSpy.spy("resolve").andCallFake(completer.resolve);
|
||||
completerSpy.spy("reject").andCallFake(completer.reject);
|
||||
return completerSpy;
|
||||
}
|
||||
|
||||
it("should wait for asyncronous app initializers",
|
||||
inject([AsyncTestCompleter, Injector], (async, injector) => {
|
||||
let ref = new PlatformRef_(injector, null);
|
||||
|
||||
let completer = createSpyPromiseCompleter();
|
||||
let SYNC_PROVIDERS = [
|
||||
new Provider(Bar, {useValue: new Bar()}),
|
||||
new Provider(APP_INITIALIZER,
|
||||
{useValue: mockAsyncAppInitializer(completer), multi: true})
|
||||
];
|
||||
ref.asyncApplication(null, SYNC_PROVIDERS)
|
||||
.then((appRef) => {
|
||||
expectProviders(appRef.injector,
|
||||
SYNC_PROVIDERS.slice(0, SYNC_PROVIDERS.length - 1));
|
||||
expect(completer.spy("resolve")).toHaveBeenCalled();
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it("should wait for async providers and then async app initializers",
|
||||
inject([AsyncTestCompleter, Injector], (async, injector) => {
|
||||
let ref = new PlatformRef_(injector, null);
|
||||
let ASYNC_PROVIDERS = [new Provider(Foo, {useValue: new Foo()})];
|
||||
let completer = createSpyPromiseCompleter();
|
||||
let SYNC_PROVIDERS = [
|
||||
new Provider(Bar, {useValue: new Bar()}),
|
||||
new Provider(APP_INITIALIZER,
|
||||
{
|
||||
useFactory: (injector) => mockAsyncAppInitializer(
|
||||
completer, ASYNC_PROVIDERS, injector),
|
||||
multi: true,
|
||||
deps: [Injector]
|
||||
})
|
||||
];
|
||||
ref.asyncApplication((zone) => PromiseWrapper.resolve(ASYNC_PROVIDERS), SYNC_PROVIDERS)
|
||||
.then((appRef) => {
|
||||
expectProviders(appRef.injector,
|
||||
SYNC_PROVIDERS.slice(0, SYNC_PROVIDERS.length - 1));
|
||||
expect(completer.spy("resolve")).toHaveBeenCalled();
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe("application", () => {
|
||||
it("should throw if an APP_INITIIALIZER returns a promise", inject([Injector], (injector) => {
|
||||
let ref = new PlatformRef_(injector, null);
|
||||
let appInitializer = new Provider(
|
||||
APP_INITIALIZER, {useValue: () => PromiseWrapper.resolve([]), multi: true});
|
||||
expect(() => ref.application([appInitializer]))
|
||||
.toThrowError(
|
||||
"Cannot use asyncronous app initializers with application. Use asyncApplication instead.");
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue