refactor(change_detection): introduce enum ChangeDetectionStrategy

BREAKING CHANGE

Closes #2497

- change detection strategy type changes from string to ChangeDetectionStrategy
- CHECK_ONCE => ChangeDetectionStrategy.CheckOnce
- CHECKED => ChangeDetectionStrategy.Checked
- CHECK_ALWAYS => ChangeDetectionStrategy.CheckAlways
- DETACHED => ChangeDetectionStrategy.Detached
- ON_PUSH => ChangeDetectionStrategy.OnPush
- DEFAULT => ChangeDetectionStrategy.Default
- ON_PUSH_OBSERVE => ChangeDetectionStrategy.OnPushObserve
This commit is contained in:
Misko Hevery
2015-08-26 11:44:59 -07:00
parent e41d7451bf
commit 69926dd002
35 changed files with 388 additions and 365 deletions
@@ -1,8 +1,7 @@
import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection';
import {isBlank, isPresent} from 'angular2/src/core/facade/lang';
import {
DEFAULT,
ON_PUSH,
ChangeDetectionStrategy,
BindingRecord,
ChangeDetectorDefinition,
DirectiveIndex,
@@ -12,7 +11,6 @@ import {
Parser,
ChangeDetectorGenConfig
} from 'angular2/src/core/change_detection/change_detection';
import {ON_PUSH_OBSERVE} from 'angular2/src/core/change_detection/constants';
import {reflector} from 'angular2/src/core/reflection/reflection';
import {ReflectionCapabilities} from 'angular2/src/core/reflection/reflection_capabilities';
@@ -109,15 +107,17 @@ export function getDefinition(id: string): TestDefinition {
} else if (id == "onPushObserveBinding") {
var records = _createBindingRecords("a");
let cdDef = new ChangeDetectorDefinition(id, ON_PUSH_OBSERVE, [], records, [], [], genConfig);
let cdDef = new ChangeDetectorDefinition(id, ChangeDetectionStrategy.OnPushObserve, [], records,
[], [], genConfig);
testDef = new TestDefinition(id, cdDef, null);
} else if (id == "onPushObserveComponent") {
let cdDef = new ChangeDetectorDefinition(id, ON_PUSH_OBSERVE, [], [], [], [], genConfig);
let cdDef = new ChangeDetectorDefinition(id, ChangeDetectionStrategy.OnPushObserve, [], [], [],
[], genConfig);
testDef = new TestDefinition(id, cdDef, null);
} else if (id == "onPushObserveDirective") {
let cdDef = new ChangeDetectorDefinition(id, ON_PUSH_OBSERVE, [], [], [],
let cdDef = new ChangeDetectorDefinition(id, ChangeDetectionStrategy.OnPushObserve, [], [], [],
[_DirectiveUpdating.recordNoCallbacks], genConfig);
testDef = new TestDefinition(id, cdDef, null);
} else if (id == "updateElementProduction") {
@@ -196,7 +196,7 @@ class _ExpressionWithLocals {
}
class _ExpressionWithMode {
constructor(private _strategy: string, private _withRecords: boolean,
constructor(private _strategy: ChangeDetectionStrategy, private _withRecords: boolean,
private _withEvents: boolean) {}
createChangeDetectorDefinition(): ChangeDetectorDefinition {
@@ -205,10 +205,14 @@ class _ExpressionWithMode {
var directiveRecords = [];
var eventRecords = [];
var dirRecordWithDefault =
new DirectiveRecord({directiveIndex: new DirectiveIndex(0, 0), changeDetection: DEFAULT});
var dirRecordWithOnPush =
new DirectiveRecord({directiveIndex: new DirectiveIndex(0, 1), changeDetection: ON_PUSH});
var dirRecordWithDefault = new DirectiveRecord({
directiveIndex: new DirectiveIndex(0, 0),
changeDetection: ChangeDetectionStrategy.Default
});
var dirRecordWithOnPush = new DirectiveRecord({
directiveIndex: new DirectiveIndex(0, 1),
changeDetection: ChangeDetectionStrategy.OnPush
});
if (this._withRecords) {
var updateDirWithOnDefaultRecord =
@@ -240,11 +244,14 @@ class _ExpressionWithMode {
* Definitions in this map define conditions which allow testing various change detector modes.
*/
static availableDefinitions: StringMap<string, _ExpressionWithMode> = {
'emptyUsingDefaultStrategy': new _ExpressionWithMode(DEFAULT, false, false),
'emptyUsingOnPushStrategy': new _ExpressionWithMode(ON_PUSH, false, false),
'onPushRecordsUsingDefaultStrategy': new _ExpressionWithMode(DEFAULT, true, false),
'onPushWithEvent': new _ExpressionWithMode(ON_PUSH, false, true),
'onPushWithHostEvent': new _ExpressionWithMode(ON_PUSH, false, true)
'emptyUsingDefaultStrategy':
new _ExpressionWithMode(ChangeDetectionStrategy.Default, false, false),
'emptyUsingOnPushStrategy':
new _ExpressionWithMode(ChangeDetectionStrategy.OnPush, false, false),
'onPushRecordsUsingDefaultStrategy':
new _ExpressionWithMode(ChangeDetectionStrategy.Default, true, false),
'onPushWithEvent': new _ExpressionWithMode(ChangeDetectionStrategy.OnPush, false, true),
'onPushWithHostEvent': new _ExpressionWithMode(ChangeDetectionStrategy.OnPush, false, true)
};
}
@@ -413,4 +420,4 @@ var _availableEventDefinitions = [
'(event)="true"'
];
var _availableHostEventDefinitions = ['(host-event)="onEvent(\$event)"'];
var _availableHostEventDefinitions = ['(host-event)="onEvent(\$event)"'];
@@ -32,12 +32,7 @@ import {
DirectiveIndex,
PipeTransform,
PipeOnDestroy,
CHECK_ALWAYS,
CHECK_ONCE,
CHECKED,
DETACHED,
ON_PUSH,
DEFAULT,
ChangeDetectionStrategy,
WrappedValue,
DynamicProtoChangeDetector,
ChangeDetectorDefinition,
@@ -673,26 +668,26 @@ export function main() {
});
describe('mode', () => {
it('should set the mode to CHECK_ALWAYS when the default change detection is used', () => {
it('should set the mode to CheckAlways when the default change detection is used', () => {
var cd = _createWithoutHydrate('emptyUsingDefaultStrategy').changeDetector;
expect(cd.mode).toEqual(null);
cd.hydrate(_DEFAULT_CONTEXT, null, null, null);
expect(cd.mode).toEqual(CHECK_ALWAYS);
expect(cd.mode).toEqual(ChangeDetectionStrategy.CheckAlways);
});
it('should set the mode to CHECK_ONCE when the push change detection is used', () => {
it('should set the mode to CheckOnce when the push change detection is used', () => {
var cd = _createWithoutHydrate('emptyUsingOnPushStrategy').changeDetector;
cd.hydrate(_DEFAULT_CONTEXT, null, null, null);
expect(cd.mode).toEqual(CHECK_ONCE);
expect(cd.mode).toEqual(ChangeDetectionStrategy.CheckOnce);
});
it('should not check a detached change detector', () => {
var val = _createChangeDetector('a', new TestData('value'));
val.changeDetector.hydrate(_DEFAULT_CONTEXT, null, null, null);
val.changeDetector.mode = DETACHED;
val.changeDetector.mode = ChangeDetectionStrategy.Detached;
val.changeDetector.detectChanges();
expect(val.dispatcher.log).toEqual([]);
@@ -702,33 +697,33 @@ export function main() {
var val = _createChangeDetector('a', new TestData('value'));
val.changeDetector.hydrate(_DEFAULT_CONTEXT, null, null, null);
val.changeDetector.mode = CHECKED;
val.changeDetector.mode = ChangeDetectionStrategy.Checked;
val.changeDetector.detectChanges();
expect(val.dispatcher.log).toEqual([]);
});
it('should change CHECK_ONCE to CHECKED', () => {
it('should change CheckOnce to Checked', () => {
var cd = _createChangeDetector('10').changeDetector;
cd.hydrate(_DEFAULT_CONTEXT, null, null, null);
cd.mode = CHECK_ONCE;
cd.mode = ChangeDetectionStrategy.CheckOnce;
cd.detectChanges();
expect(cd.mode).toEqual(CHECKED);
expect(cd.mode).toEqual(ChangeDetectionStrategy.Checked);
});
it('should not change the CHECK_ALWAYS', () => {
it('should not change the CheckAlways', () => {
var cd = _createChangeDetector('10').changeDetector;
cd.hydrate(_DEFAULT_CONTEXT, null, null, null);
cd.mode = CHECK_ALWAYS;
cd.mode = ChangeDetectionStrategy.CheckAlways;
cd.detectChanges();
expect(cd.mode).toEqual(CHECK_ALWAYS);
expect(cd.mode).toEqual(ChangeDetectionStrategy.CheckAlways);
});
describe('marking ON_PUSH detectors as CHECK_ONCE after an update', () => {
describe('marking OnPush detectors as CheckOnce after an update', () => {
var childDirectiveDetectorRegular;
var childDirectiveDetectorOnPush;
var directives;
@@ -736,53 +731,53 @@ export function main() {
beforeEach(() => {
childDirectiveDetectorRegular = _createWithoutHydrate('10').changeDetector;
childDirectiveDetectorRegular.hydrate(_DEFAULT_CONTEXT, null, null, null);
childDirectiveDetectorRegular.mode = CHECK_ALWAYS;
childDirectiveDetectorRegular.mode = ChangeDetectionStrategy.CheckAlways;
childDirectiveDetectorOnPush =
_createWithoutHydrate('emptyUsingOnPushStrategy').changeDetector;
childDirectiveDetectorOnPush.hydrate(_DEFAULT_CONTEXT, null, null, null);
childDirectiveDetectorOnPush.mode = CHECKED;
childDirectiveDetectorOnPush.mode = ChangeDetectionStrategy.Checked;
directives =
new FakeDirectives([new TestData(null), new TestData(null)],
[childDirectiveDetectorRegular, childDirectiveDetectorOnPush]);
});
it('should set the mode to CHECK_ONCE when a binding is updated', () => {
it('should set the mode to CheckOnce when a binding is updated', () => {
var parentDetector =
_createWithoutHydrate('onPushRecordsUsingDefaultStrategy').changeDetector;
parentDetector.hydrate(_DEFAULT_CONTEXT, null, directives, null);
parentDetector.detectChanges();
// making sure that we only change the status of ON_PUSH components
expect(childDirectiveDetectorRegular.mode).toEqual(CHECK_ALWAYS);
// making sure that we only change the status of OnPush components
expect(childDirectiveDetectorRegular.mode).toEqual(ChangeDetectionStrategy.CheckAlways);
expect(childDirectiveDetectorOnPush.mode).toEqual(CHECK_ONCE);
expect(childDirectiveDetectorOnPush.mode).toEqual(ChangeDetectionStrategy.CheckOnce);
});
it('should mark ON_PUSH detectors as CHECK_ONCE after an event', () => {
it('should mark OnPush detectors as CheckOnce after an event', () => {
var cd = _createWithoutHydrate('onPushWithEvent').changeDetector;
cd.hydrate(_DEFAULT_CONTEXT, null, directives, null);
cd.mode = CHECKED;
cd.mode = ChangeDetectionStrategy.Checked;
cd.handleEvent("event", 0, null);
expect(cd.mode).toEqual(CHECK_ONCE);
expect(cd.mode).toEqual(ChangeDetectionStrategy.CheckOnce);
});
it('should mark ON_PUSH detectors as CHECK_ONCE after a host event', () => {
it('should mark OnPush detectors as CheckOnce after a host event', () => {
var cd = _createWithoutHydrate('onPushWithHostEvent').changeDetector;
cd.hydrate(_DEFAULT_CONTEXT, null, directives, null);
cd.handleEvent("host-event", 0, null);
expect(childDirectiveDetectorOnPush.mode).toEqual(CHECK_ONCE);
expect(childDirectiveDetectorOnPush.mode).toEqual(ChangeDetectionStrategy.CheckOnce);
});
if (IS_DART) {
describe('ON_PUSH_OBSERVE', () => {
it('should mark ON_PUSH_OBSERVE detectors as CHECK_ONCE when an observable fires an event',
describe('OnPushObserve', () => {
it('should mark OnPushObserve detectors as CheckOnce when an observable fires an event',
fakeAsync(() => {
var context = new TestDirective();
context.a = createObservableModel();
@@ -791,15 +786,15 @@ export function main() {
cd.hydrate(context, null, directives, null);
cd.detectChanges();
expect(cd.mode).toEqual(CHECKED);
expect(cd.mode).toEqual(ChangeDetectionStrategy.Checked);
context.a.pushUpdate();
tick();
expect(cd.mode).toEqual(CHECK_ONCE);
expect(cd.mode).toEqual(ChangeDetectionStrategy.CheckOnce);
}));
it('should mark ON_PUSH_OBSERVE detectors as CHECK_ONCE when an observable context fires an event',
it('should mark OnPushObserve detectors as CheckOnce when an observable context fires an event',
fakeAsync(() => {
var context = createObservableModel();
@@ -807,15 +802,15 @@ export function main() {
cd.hydrate(context, null, directives, null);
cd.detectChanges();
expect(cd.mode).toEqual(CHECKED);
expect(cd.mode).toEqual(ChangeDetectionStrategy.Checked);
context.pushUpdate();
tick();
expect(cd.mode).toEqual(CHECK_ONCE);
expect(cd.mode).toEqual(ChangeDetectionStrategy.CheckOnce);
}));
it('should mark ON_PUSH_OBSERVE detectors as CHECK_ONCE when an observable directive fires an event',
it('should mark OnPushObserve detectors as CheckOnce when an observable directive fires an event',
fakeAsync(() => {
var dir = createObservableModel();
var directives = new FakeDirectives([dir], []);
@@ -824,12 +819,12 @@ export function main() {
cd.hydrate(_DEFAULT_CONTEXT, null, directives, null);
cd.detectChanges();
expect(cd.mode).toEqual(CHECKED);
expect(cd.mode).toEqual(ChangeDetectionStrategy.Checked);
dir.pushUpdate();
tick();
expect(cd.mode).toEqual(CHECK_ONCE);
expect(cd.mode).toEqual(ChangeDetectionStrategy.CheckOnce);
}));
it('should unsubscribe from an old observable when an object changes',
@@ -843,14 +838,14 @@ export function main() {
cd.detectChanges();
context.a = createObservableModel();
cd.mode = CHECK_ONCE;
cd.mode = ChangeDetectionStrategy.CheckOnce;
cd.detectChanges();
// Updating this model will not reenable the detector. This model is not longer
// used.
originalModel.pushUpdate();
tick();
expect(cd.mode).toEqual(CHECKED);
expect(cd.mode).toEqual(ChangeDetectionStrategy.Checked);
}));
it('should unsubscribe from observables when dehydrating', fakeAsync(() => {
@@ -872,7 +867,7 @@ export function main() {
// used.
originalModel.pushUpdate();
tick();
expect(cd.mode).toEqual(CHECKED);
expect(cd.mode).toEqual(ChangeDetectionStrategy.Checked);
}));
});
}
@@ -887,22 +882,22 @@ export function main() {
return val.changeDetector;
}
it('should mark all checked detectors as CHECK_ONCE until reaching a detached one', () => {
var root = changeDetector(CHECK_ALWAYS, null);
var disabled = changeDetector(DETACHED, root);
var parent = changeDetector(CHECKED, disabled);
var checkAlwaysChild = changeDetector(CHECK_ALWAYS, parent);
var checkOnceChild = changeDetector(CHECK_ONCE, checkAlwaysChild);
var checkedChild = changeDetector(CHECKED, checkOnceChild);
it('should mark all checked detectors as CheckOnce until reaching a detached one', () => {
var root = changeDetector(ChangeDetectionStrategy.CheckAlways, null);
var disabled = changeDetector(ChangeDetectionStrategy.Detached, root);
var parent = changeDetector(ChangeDetectionStrategy.Checked, disabled);
var checkAlwaysChild = changeDetector(ChangeDetectionStrategy.CheckAlways, parent);
var checkOnceChild = changeDetector(ChangeDetectionStrategy.CheckOnce, checkAlwaysChild);
var checkedChild = changeDetector(ChangeDetectionStrategy.Checked, checkOnceChild);
checkedChild.markPathToRootAsCheckOnce();
expect(root.mode).toEqual(CHECK_ALWAYS);
expect(disabled.mode).toEqual(DETACHED);
expect(parent.mode).toEqual(CHECK_ONCE);
expect(checkAlwaysChild.mode).toEqual(CHECK_ALWAYS);
expect(checkOnceChild.mode).toEqual(CHECK_ONCE);
expect(checkedChild.mode).toEqual(CHECK_ONCE);
expect(root.mode).toEqual(ChangeDetectionStrategy.CheckAlways);
expect(disabled.mode).toEqual(ChangeDetectionStrategy.Detached);
expect(parent.mode).toEqual(ChangeDetectionStrategy.CheckOnce);
expect(checkAlwaysChild.mode).toEqual(ChangeDetectionStrategy.CheckAlways);
expect(checkOnceChild.mode).toEqual(ChangeDetectionStrategy.CheckOnce);
expect(checkedChild.mode).toEqual(ChangeDetectionStrategy.CheckOnce);
});
});
@@ -7,6 +7,7 @@ import 'package:angular2/test_lib.dart';
import 'package:observe/observe.dart';
import 'package:angular2/src/core/directives/observable_list_diff.dart';
import 'package:angular2/src/core/change_detection/differs/default_iterable_differ.dart';
import 'package:angular2/src/core/change_detection/change_detection.dart';
class MockException implements Error {
var message;
@@ -281,7 +282,7 @@ class OnChangeComponent implements OnChange {
@Component(
selector: 'component-with-observable-list',
changeDetection: ON_PUSH,
changeDetection: ChangeDetectionStrategy.OnPush,
properties: const ['list'],
bindings: const [
const Binding(IterableDiffers,
@@ -58,7 +58,7 @@ import {
import {
PipeTransform,
ChangeDetectorRef,
ON_PUSH,
ChangeDetectionStrategy,
ChangeDetection,
DynamicChangeDetection,
ChangeDetectorGenConfig
@@ -635,7 +635,7 @@ export function main() {
}));
});
describe("ON_PUSH components", () => {
describe("OnPush components", () => {
it("should use ChangeDetectorRef to manually request a check",
inject([TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async) => {
@@ -1672,7 +1672,8 @@ class DirectiveWithTitleAndHostProperty {
title: string;
}
@Component({selector: 'push-cmp', properties: ['prop'], changeDetection: ON_PUSH})
@Component(
{selector: 'push-cmp', properties: ['prop'], changeDetection: ChangeDetectionStrategy.OnPush})
@View({template: '{{field}}'})
@Injectable()
class PushCmp {
@@ -1687,7 +1688,11 @@ class PushCmp {
}
}
@Component({selector: 'push-cmp-with-ref', properties: ['prop'], changeDetection: ON_PUSH})
@Component({
selector: 'push-cmp-with-ref',
properties: ['prop'],
changeDetection: ChangeDetectionStrategy.OnPush
})
@View({template: '{{field}}'})
@Injectable()
class PushCmpWithRef {
@@ -1708,7 +1713,7 @@ class PushCmpWithRef {
propagate() { this.ref.requestCheck(); }
}
@Component({selector: 'push-cmp-with-async', changeDetection: ON_PUSH})
@Component({selector: 'push-cmp-with-async', changeDetection: ChangeDetectionStrategy.OnPush})
@View({template: '{{field | async}}'})
@Injectable()
class PushCmpWithAsyncPipe {
@@ -1,95 +0,0 @@
import {MapWrapper} from 'angular2/src/core/facade/collection';
import {RenderDirectiveMetadata} from 'angular2/src/core/render/api';
import {
directiveMetadataFromMap,
directiveMetadataToMap
} from 'angular2/src/core/render/dom/convert';
import {ddescribe, describe, expect, it} from 'angular2/test_lib';
export function main() {
describe('convert', () => {
it('directiveMetadataToMap', () => {
var someComponent = new RenderDirectiveMetadata({
compileChildren: false,
hostListeners: MapWrapper.createFromPairs([['LKey', 'LVal']]),
hostProperties: MapWrapper.createFromPairs([['PKey', 'PVal']]),
hostActions: MapWrapper.createFromPairs([['AcKey', 'AcVal']]),
hostAttributes: MapWrapper.createFromPairs([['AtKey', 'AtVal']]),
id: 'someComponent',
properties: ['propKey: propVal'],
readAttributes: ['read1', 'read2'],
selector: 'some-comp',
type: RenderDirectiveMetadata.COMPONENT_TYPE,
exportAs: 'aaa',
callOnDestroy: true,
callOnChange: true,
callOnCheck: true,
callOnInit: true,
callOnAllChangesDone: true,
events: ['onFoo', 'onBar'],
changeDetection: 'CHECK_ONCE'
});
var map = directiveMetadataToMap(someComponent);
expect(map.get('compileChildren')).toEqual(false);
expect(map.get('hostListeners')).toEqual(MapWrapper.createFromPairs([['LKey', 'LVal']]));
expect(map.get('hostProperties')).toEqual(MapWrapper.createFromPairs([['PKey', 'PVal']]));
expect(map.get('hostActions')).toEqual(MapWrapper.createFromPairs([['AcKey', 'AcVal']]));
expect(map.get('hostAttributes')).toEqual(MapWrapper.createFromPairs([['AtKey', 'AtVal']]));
expect(map.get('id')).toEqual('someComponent');
expect(map.get('properties')).toEqual(['propKey: propVal']);
expect(map.get('readAttributes')).toEqual(['read1', 'read2']);
expect(map.get('selector')).toEqual('some-comp');
expect(map.get('type')).toEqual(RenderDirectiveMetadata.COMPONENT_TYPE);
expect(map.get('callOnDestroy')).toEqual(true);
expect(map.get('callOnCheck')).toEqual(true);
expect(map.get('callOnChange')).toEqual(true);
expect(map.get('callOnInit')).toEqual(true);
expect(map.get('callOnAllChangesDone')).toEqual(true);
expect(map.get('exportAs')).toEqual('aaa');
expect(map.get('events')).toEqual(['onFoo', 'onBar']);
expect(map.get('changeDetection')).toEqual('CHECK_ONCE');
});
it('mapToDirectiveMetadata', () => {
var map = MapWrapper.createFromPairs([
['compileChildren', false],
['hostProperties', MapWrapper.createFromPairs([['PKey', 'testVal']])],
['hostListeners', MapWrapper.createFromPairs([['LKey', 'testVal']])],
['hostActions', MapWrapper.createFromPairs([['AcKey', 'testVal']])],
['hostAttributes', MapWrapper.createFromPairs([['AtKey', 'testVal']])],
['id', 'testId'],
['properties', ['propKey: propVal']],
['readAttributes', ['readTest1', 'readTest2']],
['selector', 'testSelector'],
['type', RenderDirectiveMetadata.DIRECTIVE_TYPE],
['exportAs', 'aaa'],
['callOnDestroy', true],
['callOnCheck', true],
['callOnInit', true],
['callOnChange', true],
['callOnAllChangesDone', true],
['events', ['onFoo', 'onBar']],
['changeDetection', 'CHECK_ONCE']
]);
var meta = directiveMetadataFromMap(map);
expect(meta.compileChildren).toEqual(false);
expect(meta.hostProperties).toEqual(MapWrapper.createFromPairs([['PKey', 'testVal']]));
expect(meta.hostListeners).toEqual(MapWrapper.createFromPairs([['LKey', 'testVal']]));
expect(meta.hostActions).toEqual(MapWrapper.createFromPairs([['AcKey', 'testVal']]));
expect(meta.hostAttributes).toEqual(MapWrapper.createFromPairs([['AtKey', 'testVal']]));
expect(meta.id).toEqual('testId');
expect(meta.properties).toEqual(['propKey: propVal']);
expect(meta.readAttributes).toEqual(['readTest1', 'readTest2']);
expect(meta.selector).toEqual('testSelector');
expect(meta.type).toEqual(RenderDirectiveMetadata.DIRECTIVE_TYPE);
expect(meta.exportAs).toEqual('aaa');
expect(meta.callOnDestroy).toEqual(true);
expect(meta.callOnCheck).toEqual(true);
expect(meta.callOnInit).toEqual(true);
expect(meta.callOnChange).toEqual(true);
expect(meta.callOnAllChangesDone).toEqual(true);
expect(meta.events).toEqual(['onFoo', 'onBar']);
expect(meta.changeDetection).toEqual('CHECK_ONCE');
});
});
}