Skip to content

Commit 7c790f7

Browse files
JeanMecheAndrewKushnir
authored andcommitted
refactor(core): Improve ExpressionChangedAfterItHasBeenCheckedError (#50286)
Related to #50272 and #18970, this improves the error message of NG100 by including the class name of the component where the error was triggered. PR Close #50286
1 parent cd86eb4 commit 7c790f7

File tree

3 files changed

+10
-5
lines changed

3 files changed

+10
-5
lines changed

packages/core/src/render3/bindings.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export function bindingUpdated(lView: LView, bindingIndex: number, value: any):
6060
const details =
6161
getExpressionChangedErrorDetails(lView, bindingIndex, oldValueToCompare, value);
6262
throwErrorIfNoChangesMode(
63-
oldValue === NO_CHANGE, details.oldValue, details.newValue, details.propName);
63+
oldValue === NO_CHANGE, details.oldValue, details.newValue, details.propName, lView);
6464
}
6565
// There was a change, but the `devModeEqual` decided that the change is exempt from an error.
6666
// For this reason we exit as if no change. The early exit is needed to prevent the changed

packages/core/src/render3/errors.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {RuntimeError, RuntimeErrorCode} from '../errors';
1111
import {Type} from '../interface/type';
1212

1313
import {getComponentDef} from './definition';
14+
import {getDeclarationComponentDef} from './instructions/element_validation';
1415
import {TNode} from './interfaces/node';
1516
import {LView, TVIEW} from './interfaces/view';
1617
import {INTERPOLATION_DELIMITER} from './util/misc_utils';
@@ -52,11 +53,15 @@ export function throwMultipleComponentError(
5253

5354
/** Throws an ExpressionChangedAfterChecked error if checkNoChanges mode is on. */
5455
export function throwErrorIfNoChangesMode(
55-
creationMode: boolean, oldValue: any, currValue: any, propName?: string): never {
56+
creationMode: boolean, oldValue: any, currValue: any, propName: string|undefined,
57+
lView: LView): never {
58+
const hostComponentDef = getDeclarationComponentDef(lView);
59+
const componentClassName = hostComponentDef?.type?.name;
5660
const field = propName ? ` for '${propName}'` : '';
5761
let msg =
5862
`ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value${
59-
field}: '${oldValue}'. Current value: '${currValue}'.`;
63+
field}: '${oldValue}'. Current value: '${currValue}'.${
64+
componentClassName ? ` Expression location: ${componentClassName} component` : ''}`;
6065
if (creationMode) {
6166
msg +=
6267
` It seems like the view has been created after its parent and its children have been dirty checked.` +

packages/core/test/acceptance/exports_spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ describe('exports', () => {
5555
fixture.detectChanges();
5656
})
5757
.toThrowError(
58-
/ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked/);
58+
/ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.*AppComp/);
5959
});
6060

6161
it('should not support reference on the same node', () => {
@@ -66,7 +66,7 @@ describe('exports', () => {
6666
fixture.detectChanges();
6767
})
6868
.toThrowError(
69-
/ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked/);
69+
/ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.*AppComp/);
7070
});
7171

7272
it('should support input referenced by host binding on that directive', () => {

0 commit comments

Comments
 (0)