X Tutup
Skip to content

Commit df3074f

Browse files
committed
feat(core/application_ref): Allow asyncronous app initializers.
closes #5929. Closes #6063
1 parent f7424d5 commit df3074f

File tree

2 files changed

+143
-22
lines changed

2 files changed

+143
-22
lines changed

modules/angular2/src/core/application_ref.ts

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -217,25 +217,36 @@ export class PlatformRef_ extends PlatformRef {
217217

218218
application(providers: Array<Type | Provider | any[]>): ApplicationRef {
219219
var app = this._initApp(createNgZone(), providers);
220-
return app;
220+
if (PromiseWrapper.isPromise(app)) {
221+
throw new BaseException(
222+
"Cannot use asyncronous app initializers with application. Use asyncApplication instead.");
223+
}
224+
return <ApplicationRef>app;
221225
}
222226

223227
asyncApplication(bindingFn: (zone: NgZone) => Promise<Array<Type | Provider | any[]>>,
224228
additionalProviders?: Array<Type | Provider | any[]>): Promise<ApplicationRef> {
225229
var zone = createNgZone();
226230
var completer = PromiseWrapper.completer();
227-
zone.run(() => {
228-
PromiseWrapper.then(bindingFn(zone), (providers: Array<Type | Provider | any[]>) => {
229-
if (isPresent(additionalProviders)) {
230-
providers = ListWrapper.concat(providers, additionalProviders);
231-
}
232-
completer.resolve(this._initApp(zone, providers));
231+
if (bindingFn === null) {
232+
completer.resolve(this._initApp(zone, additionalProviders));
233+
} else {
234+
zone.run(() => {
235+
PromiseWrapper.then(bindingFn(zone), (providers: Array<Type | Provider | any[]>) => {
236+
if (isPresent(additionalProviders)) {
237+
providers = ListWrapper.concat(providers, additionalProviders);
238+
}
239+
let promise = this._initApp(zone, providers);
240+
completer.resolve(promise);
241+
});
233242
});
234-
});
243+
}
235244
return completer.promise;
236245
}
237246

238-
private _initApp(zone: NgZone, providers: Array<Type | Provider | any[]>): ApplicationRef {
247+
private _initApp(zone: NgZone,
248+
providers: Array<Type | Provider | any[]>): Promise<ApplicationRef>|
249+
ApplicationRef {
239250
var injector: Injector;
240251
var app: ApplicationRef;
241252
zone.run(() => {
@@ -259,8 +270,12 @@ export class PlatformRef_ extends PlatformRef {
259270
});
260271
app = new ApplicationRef_(this, zone, injector);
261272
this._applications.push(app);
262-
_runAppInitializers(injector);
263-
return app;
273+
var promise = _runAppInitializers(injector);
274+
if (promise !== null) {
275+
return PromiseWrapper.then(promise, (_) => app);
276+
} else {
277+
return app;
278+
}
264279
}
265280

266281
dispose(): void {
@@ -273,9 +288,22 @@ export class PlatformRef_ extends PlatformRef {
273288
_applicationDisposed(app: ApplicationRef): void { ListWrapper.remove(this._applications, app); }
274289
}
275290

276-
function _runAppInitializers(injector: Injector): void {
291+
function _runAppInitializers(injector: Injector): Promise<any> {
277292
let inits: Function[] = injector.getOptional(APP_INITIALIZER);
278-
if (isPresent(inits)) inits.forEach(init => init());
293+
let promises: Promise<any>[] = [];
294+
if (isPresent(inits)) {
295+
inits.forEach(init => {
296+
var retVal = init();
297+
if (PromiseWrapper.isPromise(retVal)) {
298+
promises.push(retVal);
299+
}
300+
});
301+
}
302+
if (promises.length > 0) {
303+
return PromiseWrapper.all(promises);
304+
} else {
305+
return null;
306+
}
279307
}
280308

281309
/**

modules/angular2/test/core/application_ref_spec.ts

Lines changed: 102 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@ import {
1111
AsyncTestCompleter,
1212
fakeAsync,
1313
tick,
14-
inject
14+
inject,
15+
SpyObject
1516
} from 'angular2/testing_internal';
1617
import {SpyChangeDetector} from './spies';
17-
import {ApplicationRef_, PlatformRef_} from "angular2/src/core/application_ref";
18-
import {Injector, Provider} from "angular2/core";
18+
import {ApplicationRef_, ApplicationRef, PlatformRef_} from "angular2/src/core/application_ref";
19+
import {Injector, Provider, APP_INITIALIZER} from "angular2/core";
1920
import {ChangeDetectorRef_} from "angular2/src/core/change_detection/change_detector_ref";
20-
import {PromiseWrapper} from "angular2/src/facade/async";
21+
import {PromiseWrapper, PromiseCompleter, TimerWrapper} from "angular2/src/facade/async";
2122
import {ListWrapper} from "angular2/src/facade/collection";
2223

2324
export function main() {
@@ -33,22 +34,114 @@ export function main() {
3334

3435
describe("PlatformRef", () => {
3536
describe("asyncApplication", () => {
36-
it("should merge synchronous and asynchronous providers",
37+
function expectProviders(injector: Injector, providers: Array<any>): void {
38+
for (let i = 0; i < providers.length; i++) {
39+
let provider = providers[i];
40+
expect(injector.get(provider.token)).toBe(provider.useValue);
41+
}
42+
}
43+
44+
it("should merge syncronous and asyncronous providers",
3745
inject([AsyncTestCompleter, Injector], (async, injector) => {
3846
let ref = new PlatformRef_(injector, null);
3947
let ASYNC_PROVIDERS = [new Provider(Foo, {useValue: new Foo()})];
4048
let SYNC_PROVIDERS = [new Provider(Bar, {useValue: new Bar()})];
4149
ref.asyncApplication((zone) => PromiseWrapper.resolve(ASYNC_PROVIDERS), SYNC_PROVIDERS)
4250
.then((appRef) => {
4351
var providers = ListWrapper.concat(ASYNC_PROVIDERS, SYNC_PROVIDERS);
44-
for (var i = 0; i < providers.length; i++) {
45-
var provider = providers[i];
46-
expect(appRef.injector.get(provider.token)).toBe(provider.useValue);
47-
}
52+
expectProviders(appRef.injector, providers);
53+
async.done();
54+
});
55+
}));
56+
57+
it("should allow function to be null",
58+
inject([AsyncTestCompleter, Injector], (async, injector) => {
59+
let ref = new PlatformRef_(injector, null);
60+
let SYNC_PROVIDERS = [new Provider(Bar, {useValue: new Bar()})];
61+
ref.asyncApplication(null, SYNC_PROVIDERS)
62+
.then((appRef) => {
63+
expectProviders(appRef.injector, SYNC_PROVIDERS);
64+
async.done();
65+
});
66+
}));
67+
68+
function mockAsyncAppInitializer(completer, providers: Array<any> = null,
69+
injector?: Injector) {
70+
return () => {
71+
if (providers != null) {
72+
expectProviders(injector, providers);
73+
}
74+
TimerWrapper.setTimeout(() => completer.resolve(true), 1);
75+
return completer.promise;
76+
};
77+
}
78+
79+
function createSpyPromiseCompleter(): SpyObject {
80+
let completer = PromiseWrapper.completer();
81+
let completerSpy = <any>new SpyObject();
82+
// Note that in TypeScript we need to provide a value for the promise attribute
83+
// whereas in dart we need to override the promise getter
84+
completerSpy.promise = completer.promise;
85+
completerSpy.spy("get:promise").andReturn(completer.promise);
86+
completerSpy.spy("resolve").andCallFake(completer.resolve);
87+
completerSpy.spy("reject").andCallFake(completer.reject);
88+
return completerSpy;
89+
}
90+
91+
it("should wait for asyncronous app initializers",
92+
inject([AsyncTestCompleter, Injector], (async, injector) => {
93+
let ref = new PlatformRef_(injector, null);
94+
95+
let completer = createSpyPromiseCompleter();
96+
let SYNC_PROVIDERS = [
97+
new Provider(Bar, {useValue: new Bar()}),
98+
new Provider(APP_INITIALIZER,
99+
{useValue: mockAsyncAppInitializer(completer), multi: true})
100+
];
101+
ref.asyncApplication(null, SYNC_PROVIDERS)
102+
.then((appRef) => {
103+
expectProviders(appRef.injector,
104+
SYNC_PROVIDERS.slice(0, SYNC_PROVIDERS.length - 1));
105+
expect(completer.spy("resolve")).toHaveBeenCalled();
106+
async.done();
107+
});
108+
}));
109+
110+
it("should wait for async providers and then async app initializers",
111+
inject([AsyncTestCompleter, Injector], (async, injector) => {
112+
let ref = new PlatformRef_(injector, null);
113+
let ASYNC_PROVIDERS = [new Provider(Foo, {useValue: new Foo()})];
114+
let completer = createSpyPromiseCompleter();
115+
let SYNC_PROVIDERS = [
116+
new Provider(Bar, {useValue: new Bar()}),
117+
new Provider(APP_INITIALIZER,
118+
{
119+
useFactory: (injector) => mockAsyncAppInitializer(
120+
completer, ASYNC_PROVIDERS, injector),
121+
multi: true,
122+
deps: [Injector]
123+
})
124+
];
125+
ref.asyncApplication((zone) => PromiseWrapper.resolve(ASYNC_PROVIDERS), SYNC_PROVIDERS)
126+
.then((appRef) => {
127+
expectProviders(appRef.injector,
128+
SYNC_PROVIDERS.slice(0, SYNC_PROVIDERS.length - 1));
129+
expect(completer.spy("resolve")).toHaveBeenCalled();
48130
async.done();
49131
});
50132
}));
51133
});
134+
135+
describe("application", () => {
136+
it("should throw if an APP_INITIIALIZER returns a promise", inject([Injector], (injector) => {
137+
let ref = new PlatformRef_(injector, null);
138+
let appInitializer = new Provider(
139+
APP_INITIALIZER, {useValue: () => PromiseWrapper.resolve([]), multi: true});
140+
expect(() => ref.application([appInitializer]))
141+
.toThrowError(
142+
"Cannot use asyncronous app initializers with application. Use asyncApplication instead.");
143+
}));
144+
});
52145
});
53146
}
54147

0 commit comments

Comments
 (0)
X Tutup