2016-02-25 17:24:17 -05:00
|
|
|
import {NgZone, NgZoneError} from 'angular2/src/core/zone/ng_zone';
|
2016-04-07 20:17:50 -04:00
|
|
|
import {Type, isBlank, isPresent, assertionsEnabled, print, IS_DART} from 'angular2/src/facade/lang';
|
2015-10-11 01:11:13 -04:00
|
|
|
import {provide, Provider, Injector, OpaqueToken} from 'angular2/src/core/di';
|
2016-04-07 20:17:50 -04:00
|
|
|
import {APP_COMPONENT_REF_PROMISE, APP_COMPONENT, APP_ID_RANDOM_PROVIDER, PLATFORM_INITIALIZER, APP_INITIALIZER} from './application_tokens';
|
2016-02-08 16:24:09 -05:00
|
|
|
import {PromiseWrapper, PromiseCompleter, ObservableWrapper} from 'angular2/src/facade/async';
|
2015-11-06 20:34:07 -05:00
|
|
|
import {ListWrapper} from 'angular2/src/facade/collection';
|
2015-09-02 18:19:26 -04:00
|
|
|
import {TestabilityRegistry, Testability} from 'angular2/src/core/testability/testability';
|
2016-04-07 20:17:50 -04:00
|
|
|
import {ComponentRef, DynamicComponentLoader} from 'angular2/src/core/linker/dynamic_component_loader';
|
|
|
|
import {BaseException, WrappedException, ExceptionHandler, unimplemented} from 'angular2/src/facade/exceptions';
|
2015-12-15 11:34:44 -05:00
|
|
|
import {Console} from 'angular2/src/core/console';
|
2015-10-28 13:34:13 -04:00
|
|
|
import {wtfLeave, wtfCreateScope, WtfScopeFn} from './profile/profile';
|
|
|
|
import {ChangeDetectorRef} from 'angular2/src/core/change_detection/change_detector_ref';
|
2015-12-15 11:34:44 -05:00
|
|
|
import {lockMode} from 'angular2/src/facade/lang';
|
2015-12-02 13:35:51 -05:00
|
|
|
import {ElementRef_} from 'angular2/src/core/linker/element_ref';
|
2015-09-02 18:19:26 -04:00
|
|
|
|
|
|
|
/**
|
2015-10-11 01:11:13 -04:00
|
|
|
* Construct providers specific to an individual root component.
|
2015-09-02 18:19:26 -04:00
|
|
|
*/
|
2016-04-07 20:17:50 -04:00
|
|
|
function _componentProviders(appComponentType: Type): Array<Type|Provider|any[]> {
|
2015-09-02 18:19:26 -04:00
|
|
|
return [
|
2015-10-12 14:30:34 -04:00
|
|
|
provide(APP_COMPONENT, {useValue: appComponentType}),
|
2016-04-07 20:17:50 -04:00
|
|
|
provide(APP_COMPONENT_REF_PROMISE, {
|
|
|
|
useFactory: (dynamicComponentLoader: DynamicComponentLoader, appRef: ApplicationRef_,
|
|
|
|
injector: Injector) => {
|
|
|
|
// Save the ComponentRef for disposal later.
|
|
|
|
var ref: ComponentRef;
|
|
|
|
// TODO(rado): investigate whether to support providers on root component.
|
|
|
|
return dynamicComponentLoader
|
|
|
|
.loadAsRoot(appComponentType, null, injector, () => { appRef._unloadComponent(ref); })
|
|
|
|
.then((componentRef) => {
|
|
|
|
ref = componentRef;
|
|
|
|
var testability = injector.getOptional(Testability);
|
|
|
|
if (isPresent(testability)) {
|
|
|
|
injector.get(TestabilityRegistry)
|
|
|
|
.registerApplication(componentRef.location.nativeElement, testability);
|
|
|
|
}
|
|
|
|
return componentRef;
|
|
|
|
});
|
|
|
|
},
|
|
|
|
deps: [DynamicComponentLoader, ApplicationRef, Injector]
|
|
|
|
}),
|
|
|
|
provide(appComponentType, {
|
|
|
|
useFactory: (p: Promise<any>) => p.then(ref => ref.instance),
|
|
|
|
deps: [APP_COMPONENT_REF_PROMISE]
|
|
|
|
}),
|
2015-09-02 18:19:26 -04:00
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create an Angular zone.
|
|
|
|
*/
|
|
|
|
export function createNgZone(): NgZone {
|
2015-10-08 16:33:22 -04:00
|
|
|
return new NgZone({enableLongStackTrace: assertionsEnabled()});
|
2015-09-02 18:19:26 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
var _platform: PlatformRef;
|
2015-11-13 14:21:16 -05:00
|
|
|
var _platformProviders: any[];
|
2015-09-02 18:19:26 -04:00
|
|
|
|
2015-11-13 14:21:16 -05:00
|
|
|
/**
|
|
|
|
* Initialize the Angular 'platform' on the page.
|
|
|
|
*
|
|
|
|
* See {@link PlatformRef} for details on the Angular platform.
|
|
|
|
*
|
|
|
|
* It is also possible to specify providers to be made in the new platform. These providers
|
|
|
|
* will be shared between all applications on the page. For example, an abstraction for
|
|
|
|
* the browser cookie jar should be bound at the platform level, because there is only one
|
|
|
|
* cookie jar regardless of how many applications on the page will be accessing it.
|
|
|
|
*
|
|
|
|
* The platform function can be called multiple times as long as the same list of providers
|
|
|
|
* is passed into each call. If the platform function is called with a different set of
|
|
|
|
* provides, Angular will throw an exception.
|
|
|
|
*/
|
2016-04-07 20:17:50 -04:00
|
|
|
export function platform(providers?: Array<Type|Provider|any[]>): PlatformRef {
|
2015-12-15 11:34:44 -05:00
|
|
|
lockMode();
|
2015-09-02 18:19:26 -04:00
|
|
|
if (isPresent(_platform)) {
|
2015-11-13 14:21:16 -05:00
|
|
|
if (ListWrapper.equals(_platformProviders, providers)) {
|
2015-09-02 18:19:26 -04:00
|
|
|
return _platform;
|
2015-11-13 14:21:16 -05:00
|
|
|
} else {
|
2016-04-07 20:17:50 -04:00
|
|
|
throw new BaseException('platform cannot be initialized with different sets of providers.');
|
2015-09-02 18:19:26 -04:00
|
|
|
}
|
2015-11-13 14:21:16 -05:00
|
|
|
} else {
|
|
|
|
return _createPlatform(providers);
|
2015-11-12 16:40:29 -05:00
|
|
|
}
|
2015-11-13 14:21:16 -05:00
|
|
|
}
|
2015-11-12 16:40:29 -05:00
|
|
|
|
2015-11-18 12:18:37 -05:00
|
|
|
/**
|
|
|
|
* Dispose the existing platform.
|
|
|
|
*/
|
|
|
|
export function disposePlatform(): void {
|
|
|
|
if (isPresent(_platform)) {
|
|
|
|
_platform.dispose();
|
|
|
|
_platform = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-07 20:17:50 -04:00
|
|
|
function _createPlatform(providers?: Array<Type|Provider|any[]>): PlatformRef {
|
2015-11-13 14:21:16 -05:00
|
|
|
_platformProviders = providers;
|
2015-11-18 12:18:37 -05:00
|
|
|
let injector = Injector.resolveAndCreate(providers);
|
|
|
|
_platform = new PlatformRef_(injector, () => {
|
2015-11-13 14:21:16 -05:00
|
|
|
_platform = null;
|
|
|
|
_platformProviders = null;
|
|
|
|
});
|
2015-11-18 12:18:37 -05:00
|
|
|
_runPlatformInitializers(injector);
|
2015-09-02 18:19:26 -04:00
|
|
|
return _platform;
|
|
|
|
}
|
|
|
|
|
2015-11-18 12:18:37 -05:00
|
|
|
function _runPlatformInitializers(injector: Injector): void {
|
2016-02-19 14:49:31 -05:00
|
|
|
let inits: Function[] = <Function[]>injector.getOptional(PLATFORM_INITIALIZER);
|
2015-11-18 12:18:37 -05:00
|
|
|
if (isPresent(inits)) inits.forEach(init => init());
|
|
|
|
}
|
|
|
|
|
2015-09-02 18:19:26 -04:00
|
|
|
/**
|
2015-09-15 18:51:15 -04:00
|
|
|
* 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
|
|
|
|
* to every Angular application running on the page are bound in its scope.
|
2015-08-20 20:18:27 -04:00
|
|
|
*
|
2015-09-15 18:51:15 -04:00
|
|
|
* A page's platform is initialized implicitly when {@link bootstrap}() is called, or
|
|
|
|
* explicitly by calling {@link platform}().
|
2015-08-20 20:18:27 -04:00
|
|
|
*/
|
2015-10-06 09:53:39 -04:00
|
|
|
export abstract class PlatformRef {
|
2015-10-26 13:50:25 -04:00
|
|
|
/**
|
|
|
|
* Register a listener to be called when the platform is disposed.
|
|
|
|
*/
|
|
|
|
abstract registerDisposeListener(dispose: () => void): void;
|
|
|
|
|
2015-09-02 18:19:26 -04:00
|
|
|
/**
|
2015-09-15 18:51:15 -04:00
|
|
|
* Retrieve the platform {@link Injector}, which is the parent injector for
|
2015-10-11 01:11:13 -04:00
|
|
|
* every Angular application on the page and provides singleton providers.
|
2015-09-02 18:19:26 -04:00
|
|
|
*/
|
2016-02-19 14:49:31 -05:00
|
|
|
get injector(): Injector { throw unimplemented(); };
|
2015-08-20 20:18:27 -04:00
|
|
|
|
|
|
|
/**
|
2015-09-15 18:51:15 -04:00
|
|
|
* Instantiate a new Angular application on the page.
|
|
|
|
*
|
2015-11-17 12:41:31 -05:00
|
|
|
* ### What is an application?
|
2015-09-15 18:51:15 -04:00
|
|
|
*
|
|
|
|
* Each Angular application has its own zone, change detection, compiler,
|
|
|
|
* renderer, and other framework components. An application hosts one or more
|
|
|
|
* root components, which can be initialized via `ApplicationRef.bootstrap()`.
|
|
|
|
*
|
2015-11-17 12:41:31 -05:00
|
|
|
* ### Application Providers
|
2015-09-15 18:51:15 -04:00
|
|
|
*
|
2015-10-11 01:11:13 -04:00
|
|
|
* Angular applications require numerous providers to be properly instantiated.
|
|
|
|
* When using `application()` to create a new app on the page, these providers
|
2015-09-15 18:51:15 -04:00
|
|
|
* must be provided. Fortunately, there are helper functions to configure
|
2015-10-11 01:11:13 -04:00
|
|
|
* typical providers, as shown in the example below.
|
2015-09-15 18:51:15 -04:00
|
|
|
*
|
2015-10-19 10:37:32 -04:00
|
|
|
* ### Example
|
2015-09-15 18:51:15 -04:00
|
|
|
*
|
2015-11-30 11:28:54 -05:00
|
|
|
* {@example core/ts/platform/platform.ts region='longform'}
|
2015-11-17 12:41:31 -05:00
|
|
|
* ### See Also
|
2015-09-15 18:51:15 -04:00
|
|
|
*
|
|
|
|
* See the {@link bootstrap} documentation for more details.
|
2015-08-20 20:18:27 -04:00
|
|
|
*/
|
2016-04-07 20:17:50 -04:00
|
|
|
abstract application(providers: Array<Type|Provider|any[]>): ApplicationRef;
|
2015-08-20 20:18:27 -04:00
|
|
|
|
|
|
|
/**
|
2015-10-11 01:11:13 -04:00
|
|
|
* Instantiate a new Angular application on the page, using providers which
|
2015-09-15 18:51:15 -04:00
|
|
|
* are only available asynchronously. One such use case is to initialize an
|
|
|
|
* application running in a web worker.
|
2015-09-02 18:19:26 -04:00
|
|
|
*
|
2015-11-17 12:41:31 -05:00
|
|
|
* ### Usage
|
2015-09-15 18:51:15 -04:00
|
|
|
*
|
|
|
|
* `bindingFn` is a function that will be called in the new application's zone.
|
2015-10-13 03:06:39 -04:00
|
|
|
* It should return a `Promise` to a list of providers to be used for the
|
2015-09-15 18:51:15 -04:00
|
|
|
* new application. Once this promise resolves, the application will be
|
|
|
|
* constructed in the same manner as a normal `application()`.
|
2015-08-20 20:18:27 -04:00
|
|
|
*/
|
2016-04-07 20:17:50 -04:00
|
|
|
abstract asyncApplication(
|
|
|
|
bindingFn: (zone: NgZone) => Promise<Array<Type|Provider|any[]>>,
|
|
|
|
providers?: Array<Type|Provider|any[]>): Promise<ApplicationRef>;
|
2015-10-06 09:53:39 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Destroy the Angular platform and all Angular applications on the page.
|
|
|
|
*/
|
|
|
|
abstract dispose(): void;
|
|
|
|
}
|
|
|
|
|
|
|
|
export class PlatformRef_ extends PlatformRef {
|
2015-10-09 20:21:25 -04:00
|
|
|
/** @internal */
|
2015-10-06 09:53:39 -04:00
|
|
|
_applications: ApplicationRef[] = [];
|
2015-10-28 17:16:54 -04:00
|
|
|
/** @internal */
|
2015-10-26 13:50:25 -04:00
|
|
|
_disposeListeners: Function[] = [];
|
2015-10-06 09:53:39 -04:00
|
|
|
|
|
|
|
constructor(private _injector: Injector, private _dispose: () => void) { super(); }
|
|
|
|
|
2015-10-26 13:50:25 -04:00
|
|
|
registerDisposeListener(dispose: () => void): void { this._disposeListeners.push(dispose); }
|
|
|
|
|
2015-10-06 09:53:39 -04:00
|
|
|
get injector(): Injector { return this._injector; }
|
|
|
|
|
2016-04-07 20:17:50 -04:00
|
|
|
application(providers: Array<Type|Provider|any[]>): ApplicationRef {
|
2015-10-19 22:05:39 -04:00
|
|
|
var app = this._initApp(createNgZone(), providers);
|
2015-12-20 18:10:36 -05:00
|
|
|
if (PromiseWrapper.isPromise(app)) {
|
|
|
|
throw new BaseException(
|
2016-04-07 20:17:50 -04:00
|
|
|
'Cannot use asyncronous app initializers with application. Use asyncApplication instead.');
|
2015-12-20 18:10:36 -05:00
|
|
|
}
|
|
|
|
return <ApplicationRef>app;
|
2015-10-06 09:53:39 -04:00
|
|
|
}
|
|
|
|
|
2016-04-07 20:17:50 -04:00
|
|
|
asyncApplication(
|
|
|
|
bindingFn: (zone: NgZone) => Promise<Array<Type|Provider|any[]>>,
|
|
|
|
additionalProviders?: Array<Type|Provider|any[]>): Promise<ApplicationRef> {
|
2015-09-02 18:19:26 -04:00
|
|
|
var zone = createNgZone();
|
2016-02-19 14:49:31 -05:00
|
|
|
var completer = PromiseWrapper.completer<ApplicationRef>();
|
2015-12-20 18:10:36 -05:00
|
|
|
if (bindingFn === null) {
|
|
|
|
completer.resolve(this._initApp(zone, additionalProviders));
|
|
|
|
} else {
|
|
|
|
zone.run(() => {
|
2016-04-07 20:17:50 -04:00
|
|
|
PromiseWrapper.then(bindingFn(zone), (providers: Array<Type|Provider|any[]>) => {
|
2015-12-20 18:10:36 -05:00
|
|
|
if (isPresent(additionalProviders)) {
|
|
|
|
providers = ListWrapper.concat(providers, additionalProviders);
|
|
|
|
}
|
|
|
|
let promise = this._initApp(zone, providers);
|
|
|
|
completer.resolve(promise);
|
|
|
|
});
|
2015-09-02 18:19:26 -04:00
|
|
|
});
|
2015-12-20 18:10:36 -05:00
|
|
|
}
|
2015-09-02 18:19:26 -04:00
|
|
|
return completer.promise;
|
|
|
|
}
|
|
|
|
|
2016-04-07 20:17:50 -04:00
|
|
|
private _initApp(zone: NgZone, providers: Array<Type|Provider|any[]>):
|
|
|
|
Promise<ApplicationRef>|ApplicationRef {
|
2015-09-02 18:19:26 -04:00
|
|
|
var injector: Injector;
|
2015-10-09 19:22:07 -04:00
|
|
|
var app: ApplicationRef;
|
2015-09-02 18:19:26 -04:00
|
|
|
zone.run(() => {
|
2015-11-13 14:21:16 -05:00
|
|
|
providers = ListWrapper.concat(providers, [
|
|
|
|
provide(NgZone, {useValue: zone}),
|
|
|
|
provide(ApplicationRef, {useFactory: (): ApplicationRef => app, deps: []})
|
|
|
|
]);
|
2015-09-02 18:19:26 -04:00
|
|
|
|
2016-03-31 19:49:49 -04:00
|
|
|
var exceptionHandler: ExceptionHandler;
|
2015-09-02 18:19:26 -04:00
|
|
|
try {
|
2015-10-11 01:11:13 -04:00
|
|
|
injector = this.injector.resolveAndCreateChild(providers);
|
2015-09-02 18:19:26 -04:00
|
|
|
exceptionHandler = injector.get(ExceptionHandler);
|
2016-02-25 17:24:17 -05:00
|
|
|
ObservableWrapper.subscribe(zone.onError, (error: NgZoneError) => {
|
|
|
|
exceptionHandler.call(error.error, error.stackTrace);
|
|
|
|
});
|
2015-09-02 18:19:26 -04:00
|
|
|
} catch (e) {
|
|
|
|
if (isPresent(exceptionHandler)) {
|
|
|
|
exceptionHandler.call(e, e.stack);
|
|
|
|
} else {
|
2015-11-19 18:09:34 -05:00
|
|
|
print(e.toString());
|
2015-09-02 18:19:26 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2015-10-09 19:22:07 -04:00
|
|
|
app = new ApplicationRef_(this, zone, injector);
|
2015-09-02 18:19:26 -04:00
|
|
|
this._applications.push(app);
|
2015-12-20 18:10:36 -05:00
|
|
|
var promise = _runAppInitializers(injector);
|
|
|
|
if (promise !== null) {
|
|
|
|
return PromiseWrapper.then(promise, (_) => app);
|
|
|
|
} else {
|
|
|
|
return app;
|
|
|
|
}
|
2015-09-02 18:19:26 -04:00
|
|
|
}
|
|
|
|
|
2015-08-20 20:18:27 -04:00
|
|
|
dispose(): void {
|
2015-11-18 12:18:37 -05:00
|
|
|
ListWrapper.clone(this._applications).forEach((app) => app.dispose());
|
2015-10-26 13:50:25 -04:00
|
|
|
this._disposeListeners.forEach((dispose) => dispose());
|
2015-09-02 18:19:26 -04:00
|
|
|
this._dispose();
|
|
|
|
}
|
|
|
|
|
2015-10-09 20:21:25 -04:00
|
|
|
/** @internal */
|
2015-09-02 18:19:26 -04:00
|
|
|
_applicationDisposed(app: ApplicationRef): void { ListWrapper.remove(this._applications, app); }
|
|
|
|
}
|
|
|
|
|
2015-12-20 18:10:36 -05:00
|
|
|
function _runAppInitializers(injector: Injector): Promise<any> {
|
2015-11-18 12:18:37 -05:00
|
|
|
let inits: Function[] = injector.getOptional(APP_INITIALIZER);
|
2015-12-20 18:10:36 -05:00
|
|
|
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;
|
|
|
|
}
|
2015-11-18 12:18:37 -05:00
|
|
|
}
|
|
|
|
|
2015-09-02 18:19:26 -04:00
|
|
|
/**
|
2015-09-15 18:51:15 -04:00
|
|
|
* A reference to an Angular application running on a page.
|
2015-09-02 18:19:26 -04:00
|
|
|
*
|
2015-09-15 18:51:15 -04:00
|
|
|
* For more about Angular applications, see the documentation for {@link bootstrap}.
|
2015-09-02 18:19:26 -04:00
|
|
|
*/
|
2015-10-06 09:53:39 -04:00
|
|
|
export abstract class ApplicationRef {
|
2015-09-02 18:19:26 -04:00
|
|
|
/**
|
2015-09-15 18:51:15 -04:00
|
|
|
* Register a listener to be called each time `bootstrap()` is called to bootstrap
|
|
|
|
* a new root component.
|
2015-09-02 18:19:26 -04:00
|
|
|
*/
|
2015-10-06 09:53:39 -04:00
|
|
|
abstract registerBootstrapListener(listener: (ref: ComponentRef) => void): void;
|
2015-08-20 20:18:27 -04:00
|
|
|
|
2015-10-26 13:50:25 -04:00
|
|
|
/**
|
|
|
|
* Register a listener to be called when the application is disposed.
|
|
|
|
*/
|
|
|
|
abstract registerDisposeListener(dispose: () => void): void;
|
|
|
|
|
2015-08-20 20:18:27 -04:00
|
|
|
/**
|
2015-09-15 18:51:15 -04:00
|
|
|
* Bootstrap a new component at the root level of the application.
|
|
|
|
*
|
2015-11-17 12:41:31 -05:00
|
|
|
* ### Bootstrap process
|
2015-09-15 18:51:15 -04:00
|
|
|
*
|
|
|
|
* When bootstrapping a new root component into an application, Angular mounts the
|
|
|
|
* specified application component onto DOM elements identified by the [componentType]'s
|
|
|
|
* selector and kicks off automatic change detection to finish initializing the component.
|
|
|
|
*
|
2015-11-17 12:41:31 -05:00
|
|
|
* ### Optional Providers
|
2015-09-15 18:51:15 -04:00
|
|
|
*
|
2015-10-19 22:05:39 -04:00
|
|
|
* Providers for the given component can optionally be overridden via the `providers`
|
2015-10-11 01:11:13 -04:00
|
|
|
* parameter. These providers will only apply for the root component being added and any
|
2015-09-15 18:51:15 -04:00
|
|
|
* child components under it.
|
|
|
|
*
|
2015-10-19 10:37:32 -04:00
|
|
|
* ### Example
|
2015-11-30 11:28:54 -05:00
|
|
|
* {@example core/ts/platform/platform.ts region='longform'}
|
2015-09-02 18:19:26 -04:00
|
|
|
*/
|
2016-04-07 20:17:50 -04:00
|
|
|
abstract bootstrap(componentType: Type, providers?: Array<Type|Provider|any[]>):
|
|
|
|
Promise<ComponentRef>;
|
2015-10-06 09:53:39 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the application {@link Injector}.
|
|
|
|
*/
|
2016-02-19 14:49:31 -05:00
|
|
|
get injector(): Injector { return <Injector>unimplemented(); };
|
2015-10-06 09:53:39 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the application {@link NgZone}.
|
|
|
|
*/
|
2016-02-19 14:49:31 -05:00
|
|
|
get zone(): NgZone { return <NgZone>unimplemented(); };
|
2015-10-06 09:53:39 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Dispose of this application and all of its components.
|
|
|
|
*/
|
|
|
|
abstract dispose(): void;
|
2015-10-09 19:22:07 -04:00
|
|
|
|
2015-10-28 13:34:13 -04:00
|
|
|
/**
|
|
|
|
* Invoke this method to explicitly process change detection and its side-effects.
|
|
|
|
*
|
|
|
|
* In development mode, `tick()` also performs a second change detection cycle to ensure that no
|
|
|
|
* further changes are detected. If additional changes are picked up during this second cycle,
|
|
|
|
* bindings in the app have side-effects that cannot be resolved in a single change detection
|
|
|
|
* pass.
|
|
|
|
* In this case, Angular throws an error, since an Angular application can only have one change
|
|
|
|
* detection pass during which all change detection must complete.
|
|
|
|
*/
|
|
|
|
abstract tick(): void;
|
|
|
|
|
2015-10-09 19:22:07 -04:00
|
|
|
/**
|
|
|
|
* Get a list of component types registered to this application.
|
|
|
|
*/
|
2016-02-19 14:49:31 -05:00
|
|
|
get componentTypes(): Type[] { return <Type[]>unimplemented(); };
|
2015-10-06 09:53:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
export class ApplicationRef_ extends ApplicationRef {
|
2015-10-28 13:34:13 -04:00
|
|
|
/** @internal */
|
|
|
|
static _tickScope: WtfScopeFn = wtfCreateScope('ApplicationRef#tick()');
|
|
|
|
|
|
|
|
/** @internal */
|
2015-10-06 09:53:39 -04:00
|
|
|
private _bootstrapListeners: Function[] = [];
|
2015-10-28 13:34:13 -04:00
|
|
|
/** @internal */
|
2015-10-26 13:50:25 -04:00
|
|
|
private _disposeListeners: Function[] = [];
|
2015-10-28 13:34:13 -04:00
|
|
|
/** @internal */
|
2015-10-06 09:53:39 -04:00
|
|
|
private _rootComponents: ComponentRef[] = [];
|
2015-10-28 13:34:13 -04:00
|
|
|
/** @internal */
|
2015-10-09 19:22:07 -04:00
|
|
|
private _rootComponentTypes: Type[] = [];
|
2015-10-28 13:34:13 -04:00
|
|
|
/** @internal */
|
|
|
|
private _changeDetectorRefs: ChangeDetectorRef[] = [];
|
|
|
|
/** @internal */
|
|
|
|
private _runningTick: boolean = false;
|
|
|
|
/** @internal */
|
|
|
|
private _enforceNoNewChanges: boolean = false;
|
2015-10-06 09:53:39 -04:00
|
|
|
|
|
|
|
constructor(private _platform: PlatformRef_, private _zone: NgZone, private _injector: Injector) {
|
|
|
|
super();
|
2015-10-28 13:34:13 -04:00
|
|
|
if (isPresent(this._zone)) {
|
2016-04-07 20:17:50 -04:00
|
|
|
ObservableWrapper.subscribe(
|
|
|
|
this._zone.onMicrotaskEmpty, (_) => { this._zone.run(() => { this.tick(); }); });
|
2015-10-28 13:34:13 -04:00
|
|
|
}
|
|
|
|
this._enforceNoNewChanges = assertionsEnabled();
|
2015-10-06 09:53:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
registerBootstrapListener(listener: (ref: ComponentRef) => void): void {
|
|
|
|
this._bootstrapListeners.push(listener);
|
|
|
|
}
|
|
|
|
|
2015-10-26 13:50:25 -04:00
|
|
|
registerDisposeListener(dispose: () => void): void { this._disposeListeners.push(dispose); }
|
|
|
|
|
2015-10-28 13:34:13 -04:00
|
|
|
registerChangeDetector(changeDetector: ChangeDetectorRef): void {
|
|
|
|
this._changeDetectorRefs.push(changeDetector);
|
|
|
|
}
|
|
|
|
|
2015-11-10 13:40:33 -05:00
|
|
|
unregisterChangeDetector(changeDetector: ChangeDetectorRef): void {
|
|
|
|
ListWrapper.remove(this._changeDetectorRefs, changeDetector);
|
|
|
|
}
|
|
|
|
|
2016-04-07 20:17:50 -04:00
|
|
|
bootstrap(componentType: Type, providers?: Array<Type|Provider|any[]>): Promise<ComponentRef> {
|
2015-09-02 18:19:26 -04:00
|
|
|
var completer = PromiseWrapper.completer();
|
|
|
|
this._zone.run(() => {
|
2015-10-11 01:11:13 -04:00
|
|
|
var componentProviders = _componentProviders(componentType);
|
|
|
|
if (isPresent(providers)) {
|
|
|
|
componentProviders.push(providers);
|
2015-09-02 18:19:26 -04:00
|
|
|
}
|
|
|
|
var exceptionHandler = this._injector.get(ExceptionHandler);
|
2015-10-09 19:22:07 -04:00
|
|
|
this._rootComponentTypes.push(componentType);
|
2015-09-02 18:19:26 -04:00
|
|
|
try {
|
2015-10-11 01:11:13 -04:00
|
|
|
var injector: Injector = this._injector.resolveAndCreateChild(componentProviders);
|
2015-09-02 18:19:26 -04:00
|
|
|
var compRefToken: Promise<ComponentRef> = injector.get(APP_COMPONENT_REF_PROMISE);
|
2016-02-11 20:01:17 -05:00
|
|
|
var tick = (componentRef: ComponentRef) => {
|
2015-11-10 13:40:33 -05:00
|
|
|
this._loadComponent(componentRef);
|
2015-09-02 18:19:26 -04:00
|
|
|
completer.resolve(componentRef);
|
|
|
|
};
|
|
|
|
|
|
|
|
var tickResult = PromiseWrapper.then(compRefToken, tick);
|
|
|
|
|
2016-02-25 17:24:17 -05:00
|
|
|
PromiseWrapper.then(tickResult, null, (err, stackTrace) => {
|
|
|
|
completer.reject(err, stackTrace);
|
|
|
|
exceptionHandler.call(err, stackTrace);
|
|
|
|
});
|
2015-09-02 18:19:26 -04:00
|
|
|
} catch (e) {
|
|
|
|
exceptionHandler.call(e, e.stack);
|
|
|
|
completer.reject(e, e.stack);
|
|
|
|
}
|
|
|
|
});
|
2016-02-19 14:49:31 -05:00
|
|
|
return completer.promise.then<ComponentRef>((ref: ComponentRef) => {
|
2015-12-15 11:34:44 -05:00
|
|
|
let c = this._injector.get(Console);
|
2016-02-03 14:47:53 -05:00
|
|
|
if (assertionsEnabled()) {
|
|
|
|
c.log(
|
2016-04-07 20:17:50 -04:00
|
|
|
'Angular 2 is running in the development mode. Call enableProdMode() to enable the production mode.');
|
2016-02-03 14:47:53 -05:00
|
|
|
}
|
2016-02-19 14:49:31 -05:00
|
|
|
return ref;
|
2015-12-15 11:34:44 -05:00
|
|
|
});
|
2015-09-02 18:19:26 -04:00
|
|
|
}
|
|
|
|
|
2015-11-10 13:40:33 -05:00
|
|
|
/** @internal */
|
2016-02-11 20:01:17 -05:00
|
|
|
_loadComponent(componentRef: ComponentRef): void {
|
|
|
|
var appChangeDetector =
|
|
|
|
(<ElementRef_>componentRef.location).internalElement.parentView.changeDetector;
|
2015-11-10 13:40:33 -05:00
|
|
|
this._changeDetectorRefs.push(appChangeDetector.ref);
|
|
|
|
this.tick();
|
2016-02-11 20:01:17 -05:00
|
|
|
this._rootComponents.push(componentRef);
|
|
|
|
this._bootstrapListeners.forEach((listener) => listener(componentRef));
|
2015-11-10 13:40:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/** @internal */
|
2016-02-11 20:01:17 -05:00
|
|
|
_unloadComponent(componentRef: ComponentRef): void {
|
|
|
|
if (!ListWrapper.contains(this._rootComponents, componentRef)) {
|
2015-11-10 13:40:33 -05:00
|
|
|
return;
|
|
|
|
}
|
2015-12-02 13:35:51 -05:00
|
|
|
this.unregisterChangeDetector(
|
2016-02-11 20:01:17 -05:00
|
|
|
(<ElementRef_>componentRef.location).internalElement.parentView.changeDetector.ref);
|
|
|
|
ListWrapper.remove(this._rootComponents, componentRef);
|
2015-11-10 13:40:33 -05:00
|
|
|
}
|
|
|
|
|
2015-08-20 20:18:27 -04:00
|
|
|
get injector(): Injector { return this._injector; }
|
2015-09-02 18:19:26 -04:00
|
|
|
|
|
|
|
get zone(): NgZone { return this._zone; }
|
|
|
|
|
2015-10-28 13:34:13 -04:00
|
|
|
tick(): void {
|
|
|
|
if (this._runningTick) {
|
2016-04-07 20:17:50 -04:00
|
|
|
throw new BaseException('ApplicationRef.tick is called recursively');
|
2015-10-28 13:34:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
var s = ApplicationRef_._tickScope();
|
|
|
|
try {
|
|
|
|
this._runningTick = true;
|
|
|
|
this._changeDetectorRefs.forEach((detector) => detector.detectChanges());
|
|
|
|
if (this._enforceNoNewChanges) {
|
|
|
|
this._changeDetectorRefs.forEach((detector) => detector.checkNoChanges());
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
this._runningTick = false;
|
|
|
|
wtfLeave(s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-02 18:19:26 -04:00
|
|
|
dispose(): void {
|
|
|
|
// TODO(alxhub): Dispose of the NgZone.
|
2015-11-18 12:18:37 -05:00
|
|
|
ListWrapper.clone(this._rootComponents).forEach((ref) => ref.dispose());
|
2015-10-26 13:50:25 -04:00
|
|
|
this._disposeListeners.forEach((dispose) => dispose());
|
2015-09-02 18:19:26 -04:00
|
|
|
this._platform._applicationDisposed(this);
|
|
|
|
}
|
2015-10-09 19:22:07 -04:00
|
|
|
|
2016-01-21 21:13:58 -05:00
|
|
|
get componentTypes(): Type[] { return this._rootComponentTypes; }
|
2015-08-20 20:18:27 -04:00
|
|
|
}
|