Skip to content

Commit 72e65d6

Browse files
committed
refactor(core): Move LifeCycle functionality into ApplicationRef.
BREAKING CHANGE: Before: constructor(@Inject(LifeCycle) lifecycle) { lifecycle.tick(); } After: constructor(@Inject(ApplicationRef) appRef) { appRef.tick(); } Closes angular#5008
1 parent ef23fe6 commit 72e65d6

19 files changed

Lines changed: 144 additions & 182 deletions

File tree

modules/angular2/core.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ export 'package:angular2/src/core/application_ref.dart'
1212
hide ApplicationRef_, PlatformRef_;
1313
export 'package:angular2/src/core/services.dart';
1414
export 'package:angular2/src/core/linker.dart';
15-
export 'package:angular2/src/core/lifecycle.dart';
1615
export 'package:angular2/src/core/zone.dart';
1716
export 'package:angular2/src/core/render.dart';
1817
export 'package:angular2/src/core/directives.dart';

modules/angular2/core.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export * from './src/core/application';
1212
export * from './src/core/bootstrap';
1313
export * from './src/core/services';
1414
export * from './src/core/linker';
15-
export * from './src/core/lifecycle';
15+
export {ApplicationRef} from './src/core/application_ref';
1616
export * from './src/core/zone';
1717
export * from './src/core/render';
1818
export * from './src/core/directives';

modules/angular2/src/core/application_ref.ts

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import {
2222
} from 'angular2/src/core/facade/exceptions';
2323
import {DOM} from 'angular2/src/core/dom/dom_adapter';
2424
import {internalView} from 'angular2/src/core/linker/view_ref';
25-
import {LifeCycle, LifeCycle_} from 'angular2/src/core/life_cycle/life_cycle';
2625
import {
2726
IterableDiffers,
2827
defaultIterableDiffers,
@@ -42,6 +41,8 @@ import {Compiler} from 'angular2/src/core/linker/compiler';
4241
import {DynamicComponentLoader_} from "./linker/dynamic_component_loader";
4342
import {AppViewManager_} from "./linker/view_manager";
4443
import {Compiler_} from "./linker/compiler";
44+
import {wtfLeave, wtfCreateScope, WtfScopeFn} from './profile/profile';
45+
import {ChangeDetectorRef} from 'angular2/src/core/change_detection/change_detector_ref';
4546

4647
/**
4748
* Constructs the set of providers meant for use at the platform level.
@@ -103,12 +104,7 @@ export function applicationCommonProviders(): Array<Type | Provider | any[]> {
103104
provide(KeyValueDiffers, {useValue: defaultKeyValueDiffers}),
104105
DirectiveResolver,
105106
PipeResolver,
106-
provide(DynamicComponentLoader, {useClass: DynamicComponentLoader_}),
107-
provide(LifeCycle,
108-
{
109-
useFactory: (exceptionHandler) => new LifeCycle_(null, assertionsEnabled()),
110-
deps: [ExceptionHandler]
111-
})
107+
provide(DynamicComponentLoader, {useClass: DynamicComponentLoader_})
112108
];
113109
}
114110

@@ -333,20 +329,49 @@ export abstract class ApplicationRef {
333329
*/
334330
abstract dispose(): void;
335331

332+
/**
333+
* Invoke this method to explicitly process change detection and its side-effects.
334+
*
335+
* In development mode, `tick()` also performs a second change detection cycle to ensure that no
336+
* further changes are detected. If additional changes are picked up during this second cycle,
337+
* bindings in the app have side-effects that cannot be resolved in a single change detection
338+
* pass.
339+
* In this case, Angular throws an error, since an Angular application can only have one change
340+
* detection pass during which all change detection must complete.
341+
*/
342+
abstract tick(): void;
343+
336344
/**
337345
* Get a list of component types registered to this application.
338346
*/
339347
get componentTypes(): Type[] { return unimplemented(); };
340348
}
341349

342350
export class ApplicationRef_ extends ApplicationRef {
351+
/** @internal */
352+
static _tickScope: WtfScopeFn = wtfCreateScope('ApplicationRef#tick()');
353+
354+
/** @internal */
343355
private _bootstrapListeners: Function[] = [];
356+
/** @internal */
344357
private _disposeListeners: Function[] = [];
358+
/** @internal */
345359
private _rootComponents: ComponentRef[] = [];
360+
/** @internal */
346361
private _rootComponentTypes: Type[] = [];
362+
/** @internal */
363+
private _changeDetectorRefs: ChangeDetectorRef[] = [];
364+
/** @internal */
365+
private _runningTick: boolean = false;
366+
/** @internal */
367+
private _enforceNoNewChanges: boolean = false;
347368

348369
constructor(private _platform: PlatformRef_, private _zone: NgZone, private _injector: Injector) {
349370
super();
371+
if (isPresent(this._zone)) {
372+
this._zone.overrideOnTurnDone(() => this.tick());
373+
}
374+
this._enforceNoNewChanges = assertionsEnabled();
350375
}
351376

352377
registerBootstrapListener(listener: (ref: ComponentRef) => void): void {
@@ -355,6 +380,10 @@ export class ApplicationRef_ extends ApplicationRef {
355380

356381
registerDisposeListener(dispose: () => void): void { this._disposeListeners.push(dispose); }
357382

383+
registerChangeDetector(changeDetector: ChangeDetectorRef): void {
384+
this._changeDetectorRefs.push(changeDetector);
385+
}
386+
358387
bootstrap(componentType: Type,
359388
providers?: Array<Type | Provider | any[]>): Promise<ComponentRef> {
360389
var completer = PromiseWrapper.completer();
@@ -370,9 +399,8 @@ export class ApplicationRef_ extends ApplicationRef {
370399
var compRefToken: Promise<ComponentRef> = injector.get(APP_COMPONENT_REF_PROMISE);
371400
var tick = (componentRef) => {
372401
var appChangeDetector = internalView(componentRef.hostView).changeDetector;
373-
var lc = injector.get(LifeCycle);
374-
lc.registerWith(this._zone, appChangeDetector);
375-
lc.tick();
402+
this._changeDetectorRefs.push(appChangeDetector.ref);
403+
this.tick();
376404
completer.resolve(componentRef);
377405
this._rootComponents.push(componentRef);
378406
this._bootstrapListeners.forEach((listener) => listener(componentRef));
@@ -395,6 +423,24 @@ export class ApplicationRef_ extends ApplicationRef {
395423

396424
get zone(): NgZone { return this._zone; }
397425

426+
tick(): void {
427+
if (this._runningTick) {
428+
throw new BaseException("ApplicationRef.tick is called recursively");
429+
}
430+
431+
var s = ApplicationRef_._tickScope();
432+
try {
433+
this._runningTick = true;
434+
this._changeDetectorRefs.forEach((detector) => detector.detectChanges());
435+
if (this._enforceNoNewChanges) {
436+
this._changeDetectorRefs.forEach((detector) => detector.checkNoChanges());
437+
}
438+
} finally {
439+
this._runningTick = false;
440+
wtfLeave(s);
441+
}
442+
}
443+
398444
dispose(): void {
399445
// TODO(alxhub): Dispose of the NgZone.
400446
this._rootComponents.forEach((ref) => ref.dispose());

modules/angular2/src/core/change_detection/change_detector_ref.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,14 @@ export abstract class ChangeDetectorRef {
126126
*/
127127
abstract detectChanges(): void;
128128

129+
/**
130+
* Checks the change detector and its children, and throws if any changes are detected.
131+
*
132+
* This is used in development mode to verify that running change detection doesn't introduce
133+
* other changes.
134+
*/
135+
abstract checkNoChanges(): void;
136+
129137
/**
130138
* Reattach the change detector to the change detector tree.
131139
*
@@ -192,6 +200,7 @@ export class ChangeDetectorRef_ extends ChangeDetectorRef {
192200
markForCheck(): void { this._cd.markPathToRootAsCheckOnce(); }
193201
detach(): void { this._cd.mode = ChangeDetectionStrategy.Detached; }
194202
detectChanges(): void { this._cd.detectChanges(); }
203+
checkNoChanges(): void { this._cd.checkNoChanges(); }
195204
reattach(): void {
196205
this._cd.mode = ChangeDetectionStrategy.CheckAlways;
197206
this.markForCheck();

modules/angular2/src/core/life_cycle/life_cycle.ts

Lines changed: 0 additions & 97 deletions
This file was deleted.

modules/angular2/src/core/lifecycle.ts

Lines changed: 0 additions & 2 deletions
This file was deleted.

modules/angular2/src/mock/mock_application_ref.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,7 @@ export class MockApplicationRef extends ApplicationRef {
2020

2121
dispose(): void {}
2222

23+
tick(): void {}
24+
2325
get componentTypes(): Type[] { return null; };
2426
}

modules/angular2/src/tools/common_tools.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {LifeCycle} from 'angular2/angular2';
1+
import {ApplicationRef} from 'angular2/src/core/application_ref';
22
import {ComponentRef, ComponentRef_} from 'angular2/src/core/linker/dynamic_component_loader';
33
import {isPresent, NumberWrapper} from 'angular2/src/core/facade/lang';
44
import {performance, window} from 'angular2/src/core/facade/browser';
@@ -19,9 +19,11 @@ export class AngularTools {
1919
* corresponds to the `ng.profiler` in the dev console.
2020
*/
2121
export class AngularProfiler {
22-
lifeCycle: LifeCycle;
22+
appRef: ApplicationRef;
2323

24-
constructor(ref: ComponentRef) { this.lifeCycle = (<ComponentRef_>ref).injector.get(LifeCycle); }
24+
constructor(ref: ComponentRef) {
25+
this.appRef = (<ComponentRef_>ref).injector.get(ApplicationRef);
26+
}
2527

2628
/**
2729
* Exercises change detection in a loop and then prints the average amount of
@@ -50,7 +52,7 @@ export class AngularProfiler {
5052
var start = DOM.performanceNow();
5153
var numTicks = 0;
5254
while (numTicks < 5 || (DOM.performanceNow() - start) < 500) {
53-
this.lifeCycle.tick();
55+
this.appRef.tick();
5456
numTicks++;
5557
}
5658
var end = DOM.performanceNow();
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import {
2+
ddescribe,
3+
describe,
4+
it,
5+
iit,
6+
xit,
7+
expect,
8+
beforeEach,
9+
afterEach,
10+
el,
11+
AsyncTestCompleter,
12+
fakeAsync,
13+
tick,
14+
inject
15+
} from 'angular2/testing_internal';
16+
import {SpyChangeDetector} from './spies';
17+
import {ApplicationRef_} from "angular2/src/core/application_ref";
18+
import {ChangeDetectorRef_} from "angular2/src/core/change_detection/change_detector_ref";
19+
20+
export function main() {
21+
describe("ApplicationRef", () => {
22+
it("should throw when reentering tick", () => {
23+
var cd = <any>new SpyChangeDetector();
24+
var ref = new ApplicationRef_(null, null, null);
25+
ref.registerChangeDetector(new ChangeDetectorRef_(cd));
26+
cd.spy("detectChanges").andCallFake(() => ref.tick());
27+
expect(() => ref.tick()).toThrowError("ApplicationRef.tick is called recursively");
28+
});
29+
});
30+
}

modules/angular2/test/core/application_spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {Component, Directive, View} from 'angular2/core';
1717
import {DOM} from 'angular2/src/core/dom/dom_adapter';
1818
import {DOCUMENT} from 'angular2/render';
1919
import {PromiseWrapper} from 'angular2/src/core/facade/async';
20-
import {provide, Inject, Injector, LifeCycle} from 'angular2/core';
20+
import {provide, Inject, Injector} from 'angular2/core';
2121
import {ExceptionHandler} from 'angular2/src/core/facade/exceptions';
2222
import {Testability, TestabilityRegistry} from 'angular2/src/core/testability/testability';
2323
import {IS_DART} from '../platform';
@@ -54,9 +54,9 @@ class HelloRootCmp3 {
5454
@Component({selector: 'hello-app'})
5555
@View({template: ''})
5656
class HelloRootCmp4 {
57-
lc;
57+
appRef;
5858

59-
constructor(@Inject(LifeCycle) lc) { this.lc = lc; }
59+
constructor(@Inject(ApplicationRef) appRef) { this.appRef = appRef; }
6060
}
6161

6262
@Component({selector: 'hello-app'})
@@ -179,7 +179,7 @@ export function main() {
179179
var refPromise = bootstrap(HelloRootCmp4, testProviders);
180180

181181
refPromise.then((ref) => {
182-
expect(ref.hostComponent.lc).toBe((<ComponentRef_>ref).injector.get(LifeCycle));
182+
expect(ref.hostComponent.appRef).toBe((<ComponentRef_>ref).injector.get(ApplicationRef));
183183
async.done();
184184
});
185185
}));

0 commit comments

Comments
 (0)