diff --git a/modules/change_detection/src/change_detector.js b/modules/change_detection/src/change_detector.js index 2522245490..6481c58971 100644 --- a/modules/change_detection/src/change_detector.js +++ b/modules/change_detection/src/change_detector.js @@ -19,6 +19,22 @@ class ExpressionChangedAfterItHasBeenChecked extends Error { } } +export class ChangeDetectionError extends Error { + message:string; + originalException:any; + location:string; + + constructor(record:Record, originalException:any) { + this.originalException = originalException; + this.location = record.protoRecord.expressionAsString; + this.message = `${this.originalException} in [${this.location}]`; + } + + toString():string { + return this.message; + } +} + export class ChangeDetector { _rootRecordRange:RecordRange; _enforceNoNewChanges:boolean; @@ -42,28 +58,32 @@ export class ChangeDetector { var record = this._rootRecordRange.findFirstEnabledRecord(); var currentRange, currentGroup; - while (isPresent(record)) { - if (record.check()) { - count++; - if (record.terminatesExpression()) { - if (throwOnChange) throw new ExpressionChangedAfterItHasBeenChecked(record); - currentRange = record.recordRange; - currentGroup = record.groupMemento(); - updatedRecords = this._addRecord(updatedRecords, record); + try { + while (isPresent(record)) { + if (record.check()) { + count++; + if (record.terminatesExpression()) { + if (throwOnChange) throw new ExpressionChangedAfterItHasBeenChecked(record); + currentRange = record.recordRange; + currentGroup = record.groupMemento(); + updatedRecords = this._addRecord(updatedRecords, record); + } } - } - if (isPresent(updatedRecords)) { - var nextEnabled = record.nextEnabled; - if (isBlank(nextEnabled) || // we have reached the last enabled record - currentRange != nextEnabled.recordRange || // the next record is in a different range - currentGroup != nextEnabled.groupMemento()) { // the next record is in a different group - currentRange.dispatcher.onRecordChange(currentGroup, updatedRecords); - updatedRecords = null; + if (isPresent(updatedRecords)) { + var nextEnabled = record.nextEnabled; + if (isBlank(nextEnabled) || // we have reached the last enabled record + currentRange != nextEnabled.recordRange || // the next record is in a different range + currentGroup != nextEnabled.groupMemento()) { // the next record is in a different group + currentRange.dispatcher.onRecordChange(currentGroup, updatedRecords); + updatedRecords = null; + } } - } - record = record.findNextEnabled(); + record = record.findNextEnabled(); + } + } catch(e) { + throw new ChangeDetectionError(record, e); } return count; diff --git a/modules/change_detection/test/change_detector_spec.js b/modules/change_detection/test/change_detector_spec.js index 882292723d..b8425fe1ae 100644 --- a/modules/change_detection/test/change_detector_spec.js +++ b/modules/change_detection/test/change_detector_spec.js @@ -1,6 +1,6 @@ import {ddescribe, describe, it, iit, xit, expect, beforeEach} from 'test_lib/test_lib'; -import {isPresent, isBlank, isJsObject} from 'facade/lang'; +import {isPresent, isBlank, isJsObject, BaseException} from 'facade/lang'; import {List, ListWrapper, MapWrapper} from 'facade/collection'; import {ContextWithVariableBindings} from 'change_detection/parser/context_with_variable_bindings'; import {Parser} from 'change_detection/parser/parser'; @@ -12,15 +12,16 @@ import { ProtoRecordRange, RecordRange, ChangeDispatcher, - ProtoRecord + ProtoRecord, + ChangeDetectionError } from 'change_detection/change_detector'; import {Record} from 'change_detection/record'; export function main() { - function ast(exp:string) { + function ast(exp:string, location:string = 'location') { var parser = new Parser(new Lexer()); - return parser.parseBinding(exp, 'location'); + return parser.parseBinding(exp, location); } function createChangeDetector(memo:string, exp:string, context = null, formatters = null, @@ -271,7 +272,7 @@ export function main() { var context = new TestData("not collection / null"); var c = createChangeDetector('a', 'a', context, null, true); expect(() => c["changeDetector"].detectChanges()) - .toThrowError("Collection records must be array like, map like or null"); + .toThrowError(new RegExp("Collection records must be array like, map like or null")); }); describe("list", () => { @@ -454,6 +455,20 @@ export function main() { }).toThrowError(new RegExp("Expression 'a in location' has changed after it was checked")); }); }); + + describe("error handling", () => { + it("should wrap exceptions into ChangeDetectionError", () => { + try { + var rr = createRange(new TestDispatcher(), ast("invalidProp", "someComponent"), 1); + detectChangesInRange(rr); + + throw new BaseException("fail"); + } catch (e) { + expect(e).toBeAnInstanceOf(ChangeDetectionError); + expect(e.location).toEqual("invalidProp in someComponent"); + } + }); + }); }); }); }