From 3c43a8c5496e01b4327b975f7487b34be5d93869 Mon Sep 17 00:00:00 2001 From: vsavkin Date: Wed, 18 Nov 2015 09:18:37 -0800 Subject: [PATCH] feat(bootstrap): add platform and app initializers Often some init logic needs to run when a platform or an application is boostrapped. For example, boostraping a platform requires initializing the dom adapter. Now, it can be done as follows: new Provider(PLATFORM_INITIALIZER, {useValue: initDomAdapter, multi: true}), All platform initializers will be run after the platform injector has been created. Similarly, all application initializers will be run after the app injector has been created. Closes #5355 --- modules/angular2/core.dart | 1 + modules/angular2/core.ts | 7 +++- modules/angular2/platform/browser.ts | 5 +-- modules/angular2/platform/browser_static.ts | 4 +-- modules/angular2/src/core/application_ref.ts | 34 ++++++++++++++++--- .../angular2/src/core/application_tokens.ts | 11 ++++++ .../angular2/src/platform/browser_common.ts | 11 +++--- .../angular2/test/platform/bootstrap_spec.ts | 24 ++++++++++++- modules/angular2/test/public_api_spec.ts | 2 ++ 9 files changed, 80 insertions(+), 19 deletions(-) diff --git a/modules/angular2/core.dart b/modules/angular2/core.dart index e7a55cc742..b768f15e16 100644 --- a/modules/angular2/core.dart +++ b/modules/angular2/core.dart @@ -9,6 +9,7 @@ export 'package:angular2/src/common/pipes.dart'; export 'package:angular2/src/facade/facade.dart'; export 'package:angular2/src/core/application_ref.dart' hide ApplicationRef_, PlatformRef_; +export 'package:angular2/src/core/application_tokens.dart' show APP_ID, APP_COMPONENT, APP_INITIALIZER, PLATFORM_INITIALIZER; export 'package:angular2/src/core/linker.dart'; export 'package:angular2/src/core/zone.dart'; export 'package:angular2/src/core/render.dart'; diff --git a/modules/angular2/core.ts b/modules/angular2/core.ts index 11aef8f54a..1c53a08007 100644 --- a/modules/angular2/core.ts +++ b/modules/angular2/core.ts @@ -10,7 +10,12 @@ export * from './src/common/pipes'; export * from './src/facade/facade'; export * from './src/core/linker'; export {platform, createNgZone, PlatformRef, ApplicationRef} from './src/core/application_ref'; -export {APP_ID, APP_COMPONENT} from './src/core/application_tokens'; +export { + APP_ID, + APP_COMPONENT, + APP_INITIALIZER, + PLATFORM_INITIALIZER +} from './src/core/application_tokens'; export * from './src/core/zone'; export * from './src/core/render'; export * from './src/common/directives'; diff --git a/modules/angular2/platform/browser.ts b/modules/angular2/platform/browser.ts index 7ca4bc4681..693e3804d5 100644 --- a/modules/angular2/platform/browser.ts +++ b/modules/angular2/platform/browser.ts @@ -13,8 +13,7 @@ import {Type, isPresent, CONST_EXPR} from 'angular2/src/facade/lang'; import {Promise} from 'angular2/src/facade/promise'; import { BROWSER_PROVIDERS, - BROWSER_APP_COMMON_PROVIDERS, - initDomAdapter + BROWSER_APP_COMMON_PROVIDERS } from 'angular2/src/platform/browser_common'; import {COMPILER_PROVIDERS} from 'angular2/compiler'; import {ComponentRef, platform, reflector} from 'angular2/core'; @@ -120,8 +119,6 @@ export function bootstrap( appComponentType: Type, customProviders?: Array): Promise { reflector.reflectionCapabilities = new ReflectionCapabilities(); - initDomAdapter(); - let appProviders = isPresent(customProviders) ? [BROWSER_APP_PROVIDERS, customProviders] : BROWSER_APP_PROVIDERS; return platform(BROWSER_PROVIDERS).application(appProviders).bootstrap(appComponentType); diff --git a/modules/angular2/platform/browser_static.ts b/modules/angular2/platform/browser_static.ts index bd6784301a..4ca014cc17 100644 --- a/modules/angular2/platform/browser_static.ts +++ b/modules/angular2/platform/browser_static.ts @@ -12,8 +12,7 @@ import {Type, isPresent, CONST_EXPR} from 'angular2/src/facade/lang'; import {Promise} from 'angular2/src/facade/promise'; import { BROWSER_PROVIDERS, - BROWSER_APP_COMMON_PROVIDERS, - initDomAdapter + BROWSER_APP_COMMON_PROVIDERS } from 'angular2/src/platform/browser_common'; import {ComponentRef, platform, reflector} from 'angular2/core'; @@ -31,7 +30,6 @@ export const BROWSER_APP_PROVIDERS: Array = export function bootstrapStatic(appComponentType: Type, customProviders?: Array, initReflector?: Function): Promise { - initDomAdapter(); if (isPresent(initReflector)) { initReflector(); } diff --git a/modules/angular2/src/core/application_ref.ts b/modules/angular2/src/core/application_ref.ts index 48f1ed162b..1d37ec26d5 100644 --- a/modules/angular2/src/core/application_ref.ts +++ b/modules/angular2/src/core/application_ref.ts @@ -4,7 +4,9 @@ import {provide, Provider, Injector, OpaqueToken} from 'angular2/src/core/di'; import { APP_COMPONENT_REF_PROMISE, APP_COMPONENT, - APP_ID_RANDOM_PROVIDER + APP_ID_RANDOM_PROVIDER, + PLATFORM_INITIALIZER, + APP_INITIALIZER } from './application_tokens'; import { Promise, @@ -30,7 +32,6 @@ import {wtfLeave, wtfCreateScope, WtfScopeFn} from './profile/profile'; import {ChangeDetectorRef} from 'angular2/src/core/change_detection/change_detector_ref'; import {lockDevMode} from 'angular2/src/facade/lang'; - /** * Construct providers specific to an individual root component. */ @@ -103,15 +104,32 @@ export function platform(providers?: Array): PlatformRe } } +/** + * Dispose the existing platform. + */ +export function disposePlatform(): void { + if (isPresent(_platform)) { + _platform.dispose(); + _platform = null; + } +} + function _createPlatform(providers?: Array): PlatformRef { _platformProviders = providers; - _platform = new PlatformRef_(Injector.resolveAndCreate(providers), () => { + let injector = Injector.resolveAndCreate(providers); + _platform = new PlatformRef_(injector, () => { _platform = null; _platformProviders = null; }); + _runPlatformInitializers(injector); return _platform; } +function _runPlatformInitializers(injector: Injector): void { + let inits: Function[] = injector.getOptional(PLATFORM_INITIALIZER); + if (isPresent(inits)) inits.forEach(init => init()); +} + /** * The Angular platform is the entry point for Angular on a web page. Each page * has exactly one platform, and services (such as reflection) which are common @@ -236,11 +254,12 @@ export class PlatformRef_ extends PlatformRef { }); app = new ApplicationRef_(this, zone, injector); this._applications.push(app); + _runAppInitializers(injector); return app; } dispose(): void { - this._applications.forEach((app) => app.dispose()); + ListWrapper.clone(this._applications).forEach((app) => app.dispose()); this._disposeListeners.forEach((dispose) => dispose()); this._dispose(); } @@ -249,6 +268,11 @@ export class PlatformRef_ extends PlatformRef { _applicationDisposed(app: ApplicationRef): void { ListWrapper.remove(this._applications, app); } } +function _runAppInitializers(injector: Injector): void { + let inits: Function[] = injector.getOptional(APP_INITIALIZER); + if (isPresent(inits)) inits.forEach(init => init()); +} + /** * A reference to an Angular application running on a page. * @@ -439,7 +463,7 @@ export class ApplicationRef_ extends ApplicationRef { dispose(): void { // TODO(alxhub): Dispose of the NgZone. - this._rootComponents.forEach((ref) => ref.dispose()); + ListWrapper.clone(this._rootComponents).forEach((ref) => ref.dispose()); this._disposeListeners.forEach((dispose) => dispose()); this._platform._applicationDisposed(this); } diff --git a/modules/angular2/src/core/application_tokens.ts b/modules/angular2/src/core/application_tokens.ts index 809087cfbb..0a3780a44f 100644 --- a/modules/angular2/src/core/application_tokens.ts +++ b/modules/angular2/src/core/application_tokens.ts @@ -48,3 +48,14 @@ export const APP_ID_RANDOM_PROVIDER: Provider = function _randomChar(): string { return StringWrapper.fromCharCode(97 + Math.floor(Math.random() * 25)); } + +/** + * A function that will be executed when a platform is initialized. + */ +export const PLATFORM_INITIALIZER: OpaqueToken = + CONST_EXPR(new OpaqueToken("Platform Initializer")); + +/** + * A function that will be executed when an application is initialized. + */ +export const APP_INITIALIZER: OpaqueToken = CONST_EXPR(new OpaqueToken("Application Initializer")); \ No newline at end of file diff --git a/modules/angular2/src/platform/browser_common.ts b/modules/angular2/src/platform/browser_common.ts index 41804d8eb4..8cf88c55f2 100644 --- a/modules/angular2/src/platform/browser_common.ts +++ b/modules/angular2/src/platform/browser_common.ts @@ -11,13 +11,13 @@ import { reflector, APPLICATION_COMMON_PROVIDERS, PLATFORM_COMMON_PROVIDERS, - EVENT_MANAGER_PLUGINS + EVENT_MANAGER_PLUGINS, + PLATFORM_INITIALIZER } from "angular2/core"; import {COMMON_DIRECTIVES, COMMON_PIPES, FORM_PROVIDERS} from "angular2/common"; import {Renderer} from 'angular2/render'; import {Testability} from 'angular2/src/core/testability/testability'; -// TODO change these imports once dom_adapter is moved out of core import {DOM} from 'angular2/src/core/dom/dom_adapter'; import {DomEventsPlugin} from 'angular2/src/platform/dom/events/dom_events'; import {KeyEventsPlugin} from 'angular2/src/platform/dom/events/key_events'; @@ -42,8 +42,10 @@ export { export {By} from 'angular2/src/platform/browser/debug/by'; export {BrowserDomAdapter} from './browser/browser_adapter'; -export const BROWSER_PROVIDERS: Array = - CONST_EXPR([PLATFORM_COMMON_PROVIDERS]); +export const BROWSER_PROVIDERS: Array = CONST_EXPR([ + PLATFORM_COMMON_PROVIDERS, + new Provider(PLATFORM_INITIALIZER, {useValue: initDomAdapter, multi: true}), +]); function _exceptionHandler(): ExceptionHandler { return new ExceptionHandler(DOM, false); @@ -73,7 +75,6 @@ export const BROWSER_APP_COMMON_PROVIDERS: Array { var logger = new _ArrayLogger(); @@ -213,6 +218,23 @@ export function main() { }); })); + it("should run platform initializers", inject([Log], (log: Log) => { + let p = platform([ + BROWSER_PROVIDERS, + provide(PLATFORM_INITIALIZER, {useValue: log.fn("platform_init1"), multi: true}), + provide(PLATFORM_INITIALIZER, {useValue: log.fn("platform_init2"), multi: true}) + ]); + expect(log.result()).toEqual("platform_init1; platform_init2"); + log.clear(); + p.application([ + BROWSER_APP_PROVIDERS, + provide(APP_INITIALIZER, {useValue: log.fn("app_init1"), multi: true}), + provide(APP_INITIALIZER, {useValue: log.fn("app_init2"), multi: true}) + ]); + + expect(log.result()).toEqual("app_init1; app_init2"); + })); + it('should register each application with the testability registry', inject([AsyncTestCompleter], (async) => { var refPromise1 = bootstrap(HelloRootCmp, testProviders); diff --git a/modules/angular2/test/public_api_spec.ts b/modules/angular2/test/public_api_spec.ts index c1cbc4b62a..3cc281ef35 100644 --- a/modules/angular2/test/public_api_spec.ts +++ b/modules/angular2/test/public_api_spec.ts @@ -27,6 +27,7 @@ import {SymbolsDiff} from './symbol_inspector/symbol_differ'; var NG_ALL = [ 'APP_COMPONENT', + 'APP_INITIALIZER', 'APP_ID', 'AbstractProviderError', 'AbstractProviderError.addKey()', @@ -1396,6 +1397,7 @@ var NG_ALL = [ 'resolveForwardRef():js', 'wtfCreateScope():js', 'PLATFORM_COMMON_PROVIDERS', + 'PLATFORM_INITIALIZER', 'wtfCreateScope:dart', 'wtfEndTimeRange():js', 'wtfEndTimeRange:dart',