diff --git a/modules/angular2/src/change_detection/abstract_change_detector.ts b/modules/angular2/src/change_detection/abstract_change_detector.ts index 77ae15ce47..7271ae8978 100644 --- a/modules/angular2/src/change_detection/abstract_change_detector.ts +++ b/modules/angular2/src/change_detection/abstract_change_detector.ts @@ -1,4 +1,4 @@ -import {isPresent} from 'angular2/src/facade/lang'; +import {isPresent, BaseException} from 'angular2/src/facade/lang'; import {List, ListWrapper} from 'angular2/src/facade/collection'; import {ChangeDetectorRef} from './change_detector_ref'; import {ChangeDetector} from './interfaces'; @@ -38,11 +38,11 @@ export class AbstractChangeDetector implements ChangeDetector { remove(): void { this.parent.removeChild(this); } - detectChanges(): void { this._detectChanges(false); } + detectChanges(): void { this.runDetectChanges(false); } - checkNoChanges(): void { this._detectChanges(true); } + checkNoChanges(): void { throw new BaseException("Not implemented"); } - _detectChanges(throwOnChange: boolean): void { + runDetectChanges(throwOnChange: boolean): void { if (this.mode === DETACHED || this.mode === CHECKED) return; this.detectChangesInRecords(throwOnChange); @@ -67,14 +67,14 @@ export class AbstractChangeDetector implements ChangeDetector { _detectChangesInLightDomChildren(throwOnChange: boolean): void { var c = this.lightDomChildren; for (var i = 0; i < c.length; ++i) { - c[i]._detectChanges(throwOnChange); + c[i].runDetectChanges(throwOnChange); } } _detectChangesInShadowDomChildren(throwOnChange: boolean): void { var c = this.shadowDomChildren; for (var i = 0; i < c.length; ++i) { - c[i]._detectChanges(throwOnChange); + c[i].runDetectChanges(throwOnChange); } } diff --git a/modules/angular2/src/change_detection/change_detection_jit_generator.ts b/modules/angular2/src/change_detection/change_detection_jit_generator.ts index e1027df795..350b2ffd3d 100644 --- a/modules/angular2/src/change_detection/change_detection_jit_generator.ts +++ b/modules/angular2/src/change_detection/change_detection_jit_generator.ts @@ -35,7 +35,8 @@ export class ChangeDetectorJITGenerator { _names: CodegenNameUtil; constructor(public id: string, public changeDetectionStrategy: string, - public records: List, public directiveRecords: List) { + public records: List, public directiveRecords: List, + private generateCheckNoChanges: boolean) { this._names = new CodegenNameUtil(this.records, this.directiveRecords, 'this._', UTIL); } @@ -80,6 +81,8 @@ export class ChangeDetectorJITGenerator { ${ALREADY_CHECKED_ACCESSOR} = true; } + ${this._genCheckNoChanges(typeName)} + ${typeName}.prototype.callOnAllChangesDone = function() { ${this._genCallOnAllChangesDoneBody()} } @@ -109,6 +112,7 @@ export class ChangeDetectorJITGenerator { return new ${typeName}(dispatcher, protos, directiveRecords); } `; + return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'protos', 'directiveRecords', classDefinition)( AbstractChangeDetector, ChangeDetectionUtil, this.records, this.directiveRecords); @@ -330,11 +334,23 @@ export class ChangeDetectorJITGenerator { } _genThrowOnChangeCheck(oldValue: string, newValue: string): string { - return ` - if(throwOnChange) { - ${UTIL}.throwOnChange(${CURRENT_PROTO}, ${UTIL}.simpleChange(${oldValue}, ${newValue})); - } - `; + if (this.generateCheckNoChanges) { + return ` + if(throwOnChange) { + ${UTIL}.throwOnChange(${CURRENT_PROTO}, ${UTIL}.simpleChange(${oldValue}, ${newValue})); + } + `; + } else { + return ''; + } + } + + _genCheckNoChanges(typeName: string): string { + if (this.generateCheckNoChanges) { + return `${typeName}.prototype.checkNoChanges = function() { this.runDetectChanges(true); }`; + } else { + return ''; + } } _genAddToChanges(r: ProtoRecord): string { diff --git a/modules/angular2/src/change_detection/dynamic_change_detector.ts b/modules/angular2/src/change_detection/dynamic_change_detector.ts index 1d5475cab3..9b7ec4be9d 100644 --- a/modules/angular2/src/change_detection/dynamic_change_detector.ts +++ b/modules/angular2/src/change_detection/dynamic_change_detector.ts @@ -65,6 +65,8 @@ export class DynamicChangeDetector extends AbstractChangeDetector { hydrated(): boolean { return this.values[0] !== null; } + checkNoChanges(): void { this.runDetectChanges(true); } + detectChangesInRecords(throwOnChange: boolean) { if (!this.hydrated()) { ChangeDetectionUtil.throwDehydrated(); diff --git a/modules/angular2/src/change_detection/interfaces.ts b/modules/angular2/src/change_detection/interfaces.ts index d81c382c07..b93698e9c0 100644 --- a/modules/angular2/src/change_detection/interfaces.ts +++ b/modules/angular2/src/change_detection/interfaces.ts @@ -63,5 +63,6 @@ export interface ProtoChangeDetector { instantiate(dispatcher: any): ChangeDetec export class ChangeDetectorDefinition { constructor(public id: string, public strategy: string, public variableNames: List, public bindingRecords: List, - public directiveRecords: List) {} + public directiveRecords: List, + public generateCheckNoChanges: boolean) {} } diff --git a/modules/angular2/src/change_detection/jit_proto_change_detector.ts b/modules/angular2/src/change_detection/jit_proto_change_detector.ts index 7a28cb4c2d..88b93bc4a2 100644 --- a/modules/angular2/src/change_detection/jit_proto_change_detector.ts +++ b/modules/angular2/src/change_detection/jit_proto_change_detector.ts @@ -23,7 +23,8 @@ export class JitProtoChangeDetector implements ProtoChangeDetector { (b) => { recordBuilder.add(b, definition.variableNames); }); var records = coalesce(recordBuilder.records); return new ChangeDetectorJITGenerator(definition.id, definition.strategy, records, - this.definition.directiveRecords) + this.definition.directiveRecords, + this.definition.generateCheckNoChanges) .generate(); } } diff --git a/modules/angular2/src/core/compiler/proto_view_factory.ts b/modules/angular2/src/core/compiler/proto_view_factory.ts index 0c8f39856a..2f1bb11f9b 100644 --- a/modules/angular2/src/core/compiler/proto_view_factory.ts +++ b/modules/angular2/src/core/compiler/proto_view_factory.ts @@ -1,7 +1,7 @@ import {Injectable} from 'angular2/di'; import {List, ListWrapper, MapWrapper} from 'angular2/src/facade/collection'; -import {isPresent, isBlank, BaseException} from 'angular2/src/facade/lang'; +import {isPresent, isBlank, BaseException, assertionsEnabled} from 'angular2/src/facade/lang'; import {reflector} from 'angular2/src/reflection/reflection'; import { @@ -246,7 +246,7 @@ function _getChangeDetectorDefinitions( var id = `${hostComponentMetadata.id}_${typeString}_${pvWithIndex.index}`; var variableNames = nestedPvVariableNames[pvWithIndex.index]; return new ChangeDetectorDefinition(id, strategyName, variableNames, bindingRecords, - directiveRecords); + directiveRecords, assertionsEnabled()); }); } diff --git a/modules/angular2/src/transform/template_compiler/change_detector_codegen.dart b/modules/angular2/src/transform/template_compiler/change_detector_codegen.dart index 7d8052ea2e..86fe05236c 100644 --- a/modules/angular2/src/transform/template_compiler/change_detector_codegen.dart +++ b/modules/angular2/src/transform/template_compiler/change_detector_codegen.dart @@ -74,10 +74,11 @@ class _CodegenState { final List _records; final List _directiveRecords; final CodegenNameUtil _names; + final bool _generateCheckNoChanges; _CodegenState._(this._changeDetectorDefId, this._contextTypeName, this._changeDetectorTypeName, String changeDetectionStrategy, - List records, List directiveRecords) + List records, List directiveRecords, this._generateCheckNoChanges) : _records = records, _directiveRecords = directiveRecords, _names = new CodegenNameUtil(records, directiveRecords, '_', _UTIL), @@ -91,7 +92,7 @@ class _CodegenState { .forEach((rec) => protoRecords.add(rec, def.variableNames)); var records = coalesce(protoRecords.records); return new _CodegenState._(def.id, typeName, changeDetectorTypeName, - def.strategy, records, def.directiveRecords); + def.strategy, records, def.directiveRecords, def.generateCheckNoChanges); } void _writeToBuf(StringBuffer buf) { @@ -138,6 +139,8 @@ class _CodegenState { $_ALREADY_CHECKED_ACCESSOR = true; } + ${_genCheckNoChanges()} + void callOnAllChangesDone() { ${_getCallOnAllChangesDoneBody()} } @@ -393,12 +396,24 @@ class _CodegenState { } String _genThrowOnChangeCheck(String oldValue, String newValue) { - return ''' - if(throwOnChange) { - $_UTIL.throwOnChange( - $_CURRENT_PROTO, $_UTIL.simpleChange(${oldValue}, ${newValue})); - } - '''; + if (this._generateCheckNoChanges) { + return ''' + if(throwOnChange) { + $_UTIL.throwOnChange( + $_CURRENT_PROTO, $_UTIL.simpleChange(${oldValue}, ${newValue})); + } + '''; + } else { + return ""; + } + } + + String _genCheckNoChanges() { + if (this._generateCheckNoChanges) { + return 'void checkNoChanges() { this.runDetectChanges(true); }'; + } else { + return ''; + } } String _genAddToChanges(ProtoRecord r) { diff --git a/modules/angular2/test/change_detection/change_detection_spec.ts b/modules/angular2/test/change_detection/change_detection_spec.ts index 3e8592e772..5db3ff5fc5 100644 --- a/modules/angular2/test/change_detection/change_detection_spec.ts +++ b/modules/angular2/test/change_detection/change_detection_spec.ts @@ -23,7 +23,7 @@ export function main() { beforeEach(() => { proto = new SpyProtoChangeDetector(); - def = new ChangeDetectorDefinition('id', null, [], [], []); + def = new ChangeDetectorDefinition('id', null, [], [], [], true); }); it("should return a proto change detector when one is available", () => { diff --git a/modules/angular2/test/change_detection/change_detector_config.ts b/modules/angular2/test/change_detection/change_detector_config.ts index 16f5844377..026a1a7b67 100644 --- a/modules/angular2/test/change_detection/change_detector_config.ts +++ b/modules/angular2/test/change_detection/change_detector_config.ts @@ -69,7 +69,7 @@ export function getDefinition(id: string): TestDefinition { var bindingRecords = _createBindingRecords(id); var directiveRecords = []; let cdDef = new ChangeDetectorDefinition(id, strategy, variableBindings, bindingRecords, - directiveRecords); + directiveRecords, true); testDef = new TestDefinition(id, cdDef, null); } if (isBlank(testDef)) { @@ -107,7 +107,7 @@ class _ExpressionWithLocals { var bindingRecords = _createBindingRecords(this._expression); var directiveRecords = []; return new ChangeDetectorDefinition('(empty id)', strategy, variableBindings, bindingRecords, - directiveRecords); + directiveRecords, true); } /** @@ -151,7 +151,7 @@ class _ExpressionWithMode { directiveRecords = []; } return new ChangeDetectorDefinition('(empty id)', this._strategy, variableBindings, - bindingRecords, directiveRecords); + bindingRecords, directiveRecords, true); } /** @@ -174,7 +174,7 @@ class _DirectiveUpdating { var variableBindings = []; return new ChangeDetectorDefinition('(empty id)', strategy, variableBindings, - this._bindingRecords, this._directiveRecords); + this._bindingRecords, this._directiveRecords, true); } static updateA(expression: string, dirRecord): BindingRecord { diff --git a/modules/angular2/test/transform/integration/two_annotations_files/expected/bar.ng_deps.dart b/modules/angular2/test/transform/integration/two_annotations_files/expected/bar.ng_deps.dart index f42da33fd8..25f338431c 100644 --- a/modules/angular2/test/transform/integration/two_annotations_files/expected/bar.ng_deps.dart +++ b/modules/angular2/test/transform/integration/two_annotations_files/expected/bar.ng_deps.dart @@ -91,6 +91,8 @@ class _MyComponent_ChangeDetector0 extends _gen.AbstractChangeDetector { _alreadyChecked = true; } + void checkNoChanges() {this.runDetectChanges(true);} + void callOnAllChangesDone() { dispatcher.notifyOnAllChangesDone(); } diff --git a/modules/benchmarks/src/change_detection/change_detection_benchmark.ts b/modules/benchmarks/src/change_detection/change_detection_benchmark.ts index 527248b8d9..357dae7768 100644 --- a/modules/benchmarks/src/change_detection/change_detection_benchmark.ts +++ b/modules/benchmarks/src/change_detection/change_detection_benchmark.ts @@ -249,7 +249,7 @@ function setUpChangeDetection(changeDetection: ChangeDetection, iterations, obje var parser = new Parser(new Lexer()); var parentProto = changeDetection.createProtoChangeDetector( - new ChangeDetectorDefinition('parent', null, [], [], [])); + new ChangeDetectorDefinition('parent', null, [], [], [], false)); var parentCd = parentProto.instantiate(dispatcher); var directiveRecord = new DirectiveRecord({directiveIndex: new DirectiveIndex(0, 0)}); @@ -277,7 +277,7 @@ function setUpChangeDetection(changeDetection: ChangeDetection, iterations, obje ]; var proto = changeDetection.createProtoChangeDetector( - new ChangeDetectorDefinition("proto", null, [], bindings, [directiveRecord])); + new ChangeDetectorDefinition("proto", null, [], bindings, [directiveRecord], false)); var targetObj = new Obj(); parentCd.hydrate(object, null, new FakeDirectives(targetObj), null);