X Tutup
Skip to content

Commit c2bbda0

Browse files
committed
feat(change_detection): provide error context for change detection errors
1 parent e744409 commit c2bbda0

File tree

17 files changed

+147
-36
lines changed

17 files changed

+147
-36
lines changed

modules/angular2/src/change_detection/abstract_change_detector.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,20 @@ import {ProtoRecord} from './proto_record';
77
import {Locals} from './parser/locals';
88
import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED, ON_PUSH} from './constants';
99

10+
class _Context {
11+
constructor(public element: any, public componentElement: any, public instance: any,
12+
public context: any, public locals: any, public injector: any,
13+
public expression: any) {}
14+
}
15+
1016
export class AbstractChangeDetector implements ChangeDetector {
1117
lightDomChildren: List<any> = [];
1218
shadowDomChildren: List<any> = [];
1319
parent: ChangeDetector;
1420
mode: string = null;
1521
ref: ChangeDetectorRef;
1622

17-
constructor(public id: string) { this.ref = new ChangeDetectorRef(this); }
23+
constructor(public id: string, public dispatcher: any) { this.ref = new ChangeDetectorRef(this); }
1824

1925
addChild(cd: ChangeDetector): void {
2026
this.lightDomChildren.push(cd);
@@ -83,6 +89,9 @@ export class AbstractChangeDetector implements ChangeDetector {
8389
}
8490

8591
throwError(proto: ProtoRecord, exception: any, stack: any): void {
86-
throw new ChangeDetectionError(proto, exception, stack);
92+
var c = this.dispatcher.getDebugContext(proto.bindingRecord.elementIndex, proto.directiveIndex);
93+
var context = new _Context(c["element"], c["componentElement"], c["directive"], c["context"],
94+
c["locals"], c["injector"], proto.expressionAsString);
95+
throw new ChangeDetectionError(proto, exception, stack, context);
8796
}
88-
}
97+
}

modules/angular2/src/change_detection/change_detection_jit_generator.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,7 @@ export class ChangeDetectorJITGenerator {
6868
var typeName = _sanitizeName(`ChangeDetector_${this.id}`);
6969
var classDefinition = `
7070
var ${typeName} = function ${typeName}(dispatcher, protos, directiveRecords) {
71-
${ABSTRACT_CHANGE_DETECTOR}.call(this, ${JSON.stringify(this.id)});
72-
${DISPATCHER_ACCESSOR} = dispatcher;
71+
${ABSTRACT_CHANGE_DETECTOR}.call(this, ${JSON.stringify(this.id)}, dispatcher);
7372
${PROTOS_ACCESSOR} = protos;
7473
${DIRECTIVES_ACCESSOR} = directiveRecords;
7574
${LOCALS_ACCESSOR} = null;

modules/angular2/src/change_detection/change_detection_util.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ export class ChangeDetectionUtil {
129129
}
130130

131131
static throwOnChange(proto: ProtoRecord, change) {
132-
throw new ExpressionChangedAfterItHasBeenChecked(proto, change);
132+
throw new ExpressionChangedAfterItHasBeenChecked(proto, change, null);
133133
}
134134

135135
static throwDehydrated() { throw new DehydratedException(); }

modules/angular2/src/change_detection/dynamic_change_detector.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
2020
alreadyChecked: boolean = false;
2121
private pipes: Pipes = null;
2222

23-
constructor(id: string, private changeControlStrategy: string, private dispatcher: any,
23+
constructor(id: string, private changeControlStrategy: string, dispatcher: any,
2424
private protos: List<ProtoRecord>, private directiveRecords: List<any>) {
25-
super(id);
25+
super(id, dispatcher);
2626
this.values = ListWrapper.createFixedSize(protos.length + 1);
2727
this.localPipes = ListWrapper.createFixedSize(protos.length + 1);
2828
this.prevContexts = ListWrapper.createFixedSize(protos.length + 1);

modules/angular2/src/change_detection/exceptions.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {ProtoRecord} from './proto_record';
22
import {BaseException} from "angular2/src/facade/lang";
33

44
export class ExpressionChangedAfterItHasBeenChecked extends BaseException {
5-
constructor(proto: ProtoRecord, change: any) {
5+
constructor(proto: ProtoRecord, change: any, context: any) {
66
super(`Expression '${proto.expressionAsString}' has changed after it was checked. ` +
77
`Previous value: '${change.previousValue}'. Current value: '${change.currentValue}'`);
88
}
@@ -11,9 +11,9 @@ export class ExpressionChangedAfterItHasBeenChecked extends BaseException {
1111
export class ChangeDetectionError extends BaseException {
1212
location: string;
1313

14-
constructor(proto: ProtoRecord, originalException: any, originalStack: any) {
15-
super(`${originalException} in [${proto.expressionAsString}]`, originalException,
16-
originalStack);
14+
constructor(proto: ProtoRecord, originalException: any, originalStack: any, context: any) {
15+
super(`${originalException} in [${proto.expressionAsString}]`, originalException, originalStack,
16+
context);
1717
this.location = proto.expressionAsString;
1818
}
1919
}

modules/angular2/src/core/application_common.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ function _injectorBindings(appComponentType): List<Type | Binding | List<any>> {
128128
DirectiveResolver,
129129
Parser,
130130
Lexer,
131-
bind(ExceptionHandler).toFactory(() => new ExceptionHandler(DOM)),
131+
bind(ExceptionHandler).toFactory(() => new ExceptionHandler(DOM), []),
132132
bind(XHR).toValue(new XHRImpl()),
133133
ComponentUrlMapper,
134134
UrlResolver,

modules/angular2/src/core/compiler/element_injector.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -496,10 +496,9 @@ export class ElementInjector extends TreeNode<ElementInjector> implements Depend
496496

497497
private _debugContext(): any {
498498
var p = this._preBuiltObjects;
499-
var element = isPresent(p.elementRef) ? p.elementRef.nativeElement : null;
500-
var hostRef = p.view.getHostElement();
501-
var componentElement = isPresent(hostRef) ? hostRef.nativeElement : null;
502-
return new _Context(element, componentElement, this._injector);
499+
var index = p.elementRef.boundElementIndex - p.view.elementOffset;
500+
var c = this._preBuiltObjects.view.getDebugContext(index, null);
501+
return new _Context(c["element"], c["componentElement"], c["injector"]);
503502
}
504503

505504
private _reattachInjectors(imperativelyCreatedInjector: Injector): void {
@@ -573,6 +572,8 @@ export class ElementInjector extends TreeNode<ElementInjector> implements Depend
573572

574573
getComponent(): any { return this._strategy.getComponent(); }
575574

575+
getInjector(): Injector { return this._injector; }
576+
576577
getElementRef(): ElementRef { return this._preBuiltObjects.elementRef; }
577578

578579
getViewContainerRef(): ViewContainerRef {

modules/angular2/src/core/compiler/view.ts

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection';
1+
import {
2+
ListWrapper,
3+
MapWrapper,
4+
Map,
5+
StringMapWrapper,
6+
List,
7+
StringMap
8+
} from 'angular2/src/facade/collection';
29
import {
310
AST,
411
Locals,
@@ -202,7 +209,36 @@ export class AppView implements ChangeDispatcher, RenderEventDispatcher {
202209

203210
getHostElement(): ElementRef {
204211
var boundElementIndex = this.mainMergeMapping.hostElementIndicesByViewIndex[this.viewOffset];
205-
return this.elementRefs[boundElementIndex];
212+
return isPresent(boundElementIndex) ? this.elementRefs[boundElementIndex] : null;
213+
}
214+
215+
getDebugContext(elementIndex: number, directiveIndex: DirectiveIndex): StringMap<string, any> {
216+
try {
217+
var offsettedIndex = this.elementOffset + elementIndex;
218+
var hasRefForIndex = offsettedIndex < this.elementRefs.length;
219+
220+
var elementRef = hasRefForIndex ? this.elementRefs[this.elementOffset + elementIndex] : null;
221+
var host = this.getHostElement();
222+
var ei = hasRefForIndex ? this.elementInjectors[this.elementOffset + elementIndex] : null;
223+
224+
var element = isPresent(elementRef) ? elementRef.nativeElement : null;
225+
var componentElement = isPresent(host) ? host.nativeElement : null;
226+
var directive = isPresent(directiveIndex) ? this.getDirectiveFor(directiveIndex) : null;
227+
var injector = isPresent(ei) ? ei.getInjector() : null;
228+
229+
return {
230+
element: element,
231+
componentElement: componentElement,
232+
directive: directive,
233+
context: this.context,
234+
locals: _localsToStringMap(this.locals),
235+
injector: injector
236+
};
237+
} catch (e) {
238+
// TODO: vsavkin log the exception once we have a good way to log errors and warnings
239+
// if an error happens during getting the debug context, we return an empty map.
240+
return {};
241+
}
206242
}
207243

208244
getDetectorFor(directive: DirectiveIndex): any {
@@ -252,6 +288,16 @@ export class AppView implements ChangeDispatcher, RenderEventDispatcher {
252288
}
253289
}
254290

291+
function _localsToStringMap(locals: Locals): StringMap<string, any> {
292+
var res = {};
293+
var c = locals;
294+
while (isPresent(c)) {
295+
res = StringMapWrapper.merge(res, MapWrapper.toStringMap(c.current));
296+
c = c.parent;
297+
}
298+
return res;
299+
}
300+
255301
/**
256302
*
257303
*/

modules/angular2/src/core/exception_handler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export class ExceptionHandler {
5050

5151
if (isPresent(stackTrace) && isBlank(originalStack)) {
5252
this.logger.log("STACKTRACE:");
53-
this.logger.log(this._longStackTrace(stackTrace))
53+
this.logger.log(this._longStackTrace(stackTrace));
5454
}
5555

5656
if (isPresent(reason)) {

modules/angular2/src/di/exceptions.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,7 @@ export class InstantiationError extends AbstractBindingError {
105105
constructor(injector: Injector, originalException, originalStack, key: Key) {
106106
super(injector, key, function(keys: List<any>) {
107107
var first = stringify(ListWrapper.first(keys).token);
108-
return `Error during instantiation of ${first}!${constructResolvingPath(keys)}.` +
109-
`\n\n ORIGINAL ERROR: ${originalException}` +
110-
`\n\n ORIGINAL STACK: ${originalStack} \n`;
108+
return `Error during instantiation of ${first}!${constructResolvingPath(keys)}.`;
111109
}, originalException, originalStack);
112110

113111
this.causeKey = key;

0 commit comments

Comments
 (0)
X Tutup