refactor(core): Move LifeCycle functionality into ApplicationRef.
BREAKING CHANGE: Before: constructor(@Inject(LifeCycle) lifecycle) { lifecycle.tick(); } After: constructor(@Inject(ApplicationRef) appRef) { appRef.tick(); } Closes #5008
This commit is contained in:
@ -22,7 +22,6 @@ import {
|
||||
} from 'angular2/src/core/facade/exceptions';
|
||||
import {DOM} from 'angular2/src/core/dom/dom_adapter';
|
||||
import {internalView} from 'angular2/src/core/linker/view_ref';
|
||||
import {LifeCycle, LifeCycle_} from 'angular2/src/core/life_cycle/life_cycle';
|
||||
import {
|
||||
IterableDiffers,
|
||||
defaultIterableDiffers,
|
||||
@ -42,6 +41,8 @@ import {Compiler} from 'angular2/src/core/linker/compiler';
|
||||
import {DynamicComponentLoader_} from "./linker/dynamic_component_loader";
|
||||
import {AppViewManager_} from "./linker/view_manager";
|
||||
import {Compiler_} from "./linker/compiler";
|
||||
import {wtfLeave, wtfCreateScope, WtfScopeFn} from './profile/profile';
|
||||
import {ChangeDetectorRef} from 'angular2/src/core/change_detection/change_detector_ref';
|
||||
|
||||
/**
|
||||
* Constructs the set of providers meant for use at the platform level.
|
||||
@ -103,12 +104,7 @@ export function applicationCommonProviders(): Array<Type | Provider | any[]> {
|
||||
provide(KeyValueDiffers, {useValue: defaultKeyValueDiffers}),
|
||||
DirectiveResolver,
|
||||
PipeResolver,
|
||||
provide(DynamicComponentLoader, {useClass: DynamicComponentLoader_}),
|
||||
provide(LifeCycle,
|
||||
{
|
||||
useFactory: (exceptionHandler) => new LifeCycle_(null, assertionsEnabled()),
|
||||
deps: [ExceptionHandler]
|
||||
})
|
||||
provide(DynamicComponentLoader, {useClass: DynamicComponentLoader_})
|
||||
];
|
||||
}
|
||||
|
||||
@ -333,6 +329,18 @@ export abstract class ApplicationRef {
|
||||
*/
|
||||
abstract dispose(): void;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Get a list of component types registered to this application.
|
||||
*/
|
||||
@ -340,13 +348,30 @@ export abstract class ApplicationRef {
|
||||
}
|
||||
|
||||
export class ApplicationRef_ extends ApplicationRef {
|
||||
/** @internal */
|
||||
static _tickScope: WtfScopeFn = wtfCreateScope('ApplicationRef#tick()');
|
||||
|
||||
/** @internal */
|
||||
private _bootstrapListeners: Function[] = [];
|
||||
/** @internal */
|
||||
private _disposeListeners: Function[] = [];
|
||||
/** @internal */
|
||||
private _rootComponents: ComponentRef[] = [];
|
||||
/** @internal */
|
||||
private _rootComponentTypes: Type[] = [];
|
||||
/** @internal */
|
||||
private _changeDetectorRefs: ChangeDetectorRef[] = [];
|
||||
/** @internal */
|
||||
private _runningTick: boolean = false;
|
||||
/** @internal */
|
||||
private _enforceNoNewChanges: boolean = false;
|
||||
|
||||
constructor(private _platform: PlatformRef_, private _zone: NgZone, private _injector: Injector) {
|
||||
super();
|
||||
if (isPresent(this._zone)) {
|
||||
this._zone.overrideOnTurnDone(() => this.tick());
|
||||
}
|
||||
this._enforceNoNewChanges = assertionsEnabled();
|
||||
}
|
||||
|
||||
registerBootstrapListener(listener: (ref: ComponentRef) => void): void {
|
||||
@ -355,6 +380,10 @@ export class ApplicationRef_ extends ApplicationRef {
|
||||
|
||||
registerDisposeListener(dispose: () => void): void { this._disposeListeners.push(dispose); }
|
||||
|
||||
registerChangeDetector(changeDetector: ChangeDetectorRef): void {
|
||||
this._changeDetectorRefs.push(changeDetector);
|
||||
}
|
||||
|
||||
bootstrap(componentType: Type,
|
||||
providers?: Array<Type | Provider | any[]>): Promise<ComponentRef> {
|
||||
var completer = PromiseWrapper.completer();
|
||||
@ -370,9 +399,8 @@ export class ApplicationRef_ extends ApplicationRef {
|
||||
var compRefToken: Promise<ComponentRef> = injector.get(APP_COMPONENT_REF_PROMISE);
|
||||
var tick = (componentRef) => {
|
||||
var appChangeDetector = internalView(componentRef.hostView).changeDetector;
|
||||
var lc = injector.get(LifeCycle);
|
||||
lc.registerWith(this._zone, appChangeDetector);
|
||||
lc.tick();
|
||||
this._changeDetectorRefs.push(appChangeDetector.ref);
|
||||
this.tick();
|
||||
completer.resolve(componentRef);
|
||||
this._rootComponents.push(componentRef);
|
||||
this._bootstrapListeners.forEach((listener) => listener(componentRef));
|
||||
@ -395,6 +423,24 @@ export class ApplicationRef_ extends ApplicationRef {
|
||||
|
||||
get zone(): NgZone { return this._zone; }
|
||||
|
||||
tick(): void {
|
||||
if (this._runningTick) {
|
||||
throw new BaseException("ApplicationRef.tick is called recursively");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
// TODO(alxhub): Dispose of the NgZone.
|
||||
this._rootComponents.forEach((ref) => ref.dispose());
|
||||
|
@ -126,6 +126,14 @@ export abstract class ChangeDetectorRef {
|
||||
*/
|
||||
abstract detectChanges(): void;
|
||||
|
||||
/**
|
||||
* Checks the change detector and its children, and throws if any changes are detected.
|
||||
*
|
||||
* This is used in development mode to verify that running change detection doesn't introduce
|
||||
* other changes.
|
||||
*/
|
||||
abstract checkNoChanges(): void;
|
||||
|
||||
/**
|
||||
* Reattach the change detector to the change detector tree.
|
||||
*
|
||||
@ -192,6 +200,7 @@ export class ChangeDetectorRef_ extends ChangeDetectorRef {
|
||||
markForCheck(): void { this._cd.markPathToRootAsCheckOnce(); }
|
||||
detach(): void { this._cd.mode = ChangeDetectionStrategy.Detached; }
|
||||
detectChanges(): void { this._cd.detectChanges(); }
|
||||
checkNoChanges(): void { this._cd.checkNoChanges(); }
|
||||
reattach(): void {
|
||||
this._cd.mode = ChangeDetectionStrategy.CheckAlways;
|
||||
this.markForCheck();
|
||||
|
@ -1,97 +0,0 @@
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
import {ChangeDetector} from 'angular2/src/core/change_detection/change_detection';
|
||||
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||
import {isPresent} from 'angular2/src/core/facade/lang';
|
||||
import {BaseException, WrappedException} from 'angular2/src/core/facade/exceptions';
|
||||
import {wtfLeave, wtfCreateScope, WtfScopeFn} from '../profile/profile';
|
||||
|
||||
/**
|
||||
* Provides access to explicitly trigger change detection in an application.
|
||||
*
|
||||
* By default, `Zone` triggers change detection in Angular on each virtual machine (VM) turn. When
|
||||
* testing, or in some
|
||||
* limited application use cases, a developer can also trigger change detection with the
|
||||
* `lifecycle.tick()` method.
|
||||
*
|
||||
* Each Angular application has a single `LifeCycle` instance.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* This is a contrived example, since the bootstrap automatically runs inside of the `Zone`, which
|
||||
* invokes
|
||||
* `lifecycle.tick()` on your behalf.
|
||||
*
|
||||
* ```javascript
|
||||
* bootstrap(MyApp).then((ref:ComponentRef) => {
|
||||
* var lifeCycle = ref.injector.get(LifeCycle);
|
||||
* var myApp = ref.instance;
|
||||
*
|
||||
* ref.doSomething();
|
||||
* lifecycle.tick();
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export abstract class LifeCycle {
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class LifeCycle_ extends LifeCycle {
|
||||
/** @internal */
|
||||
static _tickScope: WtfScopeFn = wtfCreateScope('LifeCycle#tick()');
|
||||
/** @internal */
|
||||
_changeDetectors: ChangeDetector[];
|
||||
/** @internal */
|
||||
_enforceNoNewChanges: boolean;
|
||||
/** @internal */
|
||||
_runningTick: boolean = false;
|
||||
|
||||
constructor(changeDetector: ChangeDetector = null, enforceNoNewChanges: boolean = false) {
|
||||
super();
|
||||
this._changeDetectors = [];
|
||||
if (isPresent(changeDetector)) {
|
||||
this._changeDetectors.push(changeDetector);
|
||||
}
|
||||
this._enforceNoNewChanges = enforceNoNewChanges;
|
||||
}
|
||||
|
||||
registerWith(zone: NgZone, changeDetector: ChangeDetector = null) {
|
||||
if (isPresent(changeDetector)) {
|
||||
this._changeDetectors.push(changeDetector);
|
||||
}
|
||||
zone.overrideOnTurnDone(() => this.tick());
|
||||
}
|
||||
|
||||
tick() {
|
||||
if (this._runningTick) {
|
||||
throw new BaseException("LifeCycle.tick is called recursively");
|
||||
}
|
||||
|
||||
var s = LifeCycle_._tickScope();
|
||||
try {
|
||||
this._runningTick = true;
|
||||
this._changeDetectors.forEach((detector) => detector.detectChanges());
|
||||
if (this._enforceNoNewChanges) {
|
||||
this._changeDetectors.forEach((detector) => detector.checkNoChanges());
|
||||
}
|
||||
} finally {
|
||||
this._runningTick = false;
|
||||
wtfLeave(s);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
// Public API for LifeCycle
|
||||
export {LifeCycle} from './life_cycle/life_cycle';
|
@ -20,5 +20,7 @@ export class MockApplicationRef extends ApplicationRef {
|
||||
|
||||
dispose(): void {}
|
||||
|
||||
tick(): void {}
|
||||
|
||||
get componentTypes(): Type[] { return null; };
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import {LifeCycle} from 'angular2/angular2';
|
||||
import {ApplicationRef} from 'angular2/src/core/application_ref';
|
||||
import {ComponentRef, ComponentRef_} from 'angular2/src/core/linker/dynamic_component_loader';
|
||||
import {isPresent, NumberWrapper} from 'angular2/src/core/facade/lang';
|
||||
import {performance, window} from 'angular2/src/core/facade/browser';
|
||||
@ -19,9 +19,11 @@ export class AngularTools {
|
||||
* corresponds to the `ng.profiler` in the dev console.
|
||||
*/
|
||||
export class AngularProfiler {
|
||||
lifeCycle: LifeCycle;
|
||||
appRef: ApplicationRef;
|
||||
|
||||
constructor(ref: ComponentRef) { this.lifeCycle = (<ComponentRef_>ref).injector.get(LifeCycle); }
|
||||
constructor(ref: ComponentRef) {
|
||||
this.appRef = (<ComponentRef_>ref).injector.get(ApplicationRef);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exercises change detection in a loop and then prints the average amount of
|
||||
@ -50,7 +52,7 @@ export class AngularProfiler {
|
||||
var start = DOM.performanceNow();
|
||||
var numTicks = 0;
|
||||
while (numTicks < 5 || (DOM.performanceNow() - start) < 500) {
|
||||
this.lifeCycle.tick();
|
||||
this.appRef.tick();
|
||||
numTicks++;
|
||||
}
|
||||
var end = DOM.performanceNow();
|
||||
|
Reference in New Issue
Block a user