Skip to content

Commit

Permalink
fix(core): Updates error to use RuntimeError code (#46526)
Browse files Browse the repository at this point in the history
This updates the iterable differ error to use more up-to-date error
codes.

PR Close #46526
  • Loading branch information
jessicajaniuk authored and dylhunn committed Jun 28, 2022
1 parent f355a24 commit 57e8fc0
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 8 deletions.
16 changes: 16 additions & 0 deletions aio/content/errors/NG2200.md
@@ -0,0 +1,16 @@
@name Missing Iterable Differ
@category runtime
@shortDescription Cannot find a differ for object in ngFor

@description
`NgFor` could not find an iterable differ for the value passed in. Make sure it's an iterable, like an `Array`.

@debugging
When using ngFor in a template, you must use some type of Iterable, like `Array`, `Set`, `Map`, etc.
If you're trying to iterate over the keys in an object, you should look at the [KeyValue pipe](/api/common/KeyValuePipe) instead.

<!-- links -->

<!-- external links -->

<!-- end links -->
2 changes: 2 additions & 0 deletions goldens/public-api/common/errors.md
Expand Up @@ -9,6 +9,8 @@ export const enum RuntimeErrorCode {
// (undocumented)
INVALID_PIPE_ARGUMENT = 2100,
// (undocumented)
NG_FOR_MISSING_DIFFER = 2200,
// (undocumented)
PARENT_NG_SWITCH_NOT_FOUND = 2000
}

Expand Down
18 changes: 13 additions & 5 deletions packages/common/src/directives/ng_for_of.ts
Expand Up @@ -6,7 +6,11 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Directive, DoCheck, EmbeddedViewRef, Input, IterableChangeRecord, IterableChanges, IterableDiffer, IterableDiffers, NgIterable, TemplateRef, TrackByFunction, ViewContainerRef} from '@angular/core';
import {Directive, DoCheck, EmbeddedViewRef, Input, IterableChangeRecord, IterableChanges, IterableDiffer, IterableDiffers, NgIterable, TemplateRef, TrackByFunction, ViewContainerRef, ɵRuntimeError as RuntimeError} from '@angular/core';

import {RuntimeErrorCode} from '../errors';

const NG_DEV_MODE = typeof ngDevMode === 'undefined' || !!ngDevMode;

/**
* @publicApi
Expand Down Expand Up @@ -160,7 +164,7 @@ export class NgForOf<T, U extends NgIterable<T> = NgIterable<T>> implements DoCh
*/
@Input()
set ngForTrackBy(fn: TrackByFunction<T>) {
if ((typeof ngDevMode === 'undefined' || ngDevMode) && fn != null && typeof fn !== 'function') {
if (NG_DEV_MODE && fn != null && typeof fn !== 'function') {
// TODO(vicb): use a log service once there is a public one available
if (<any>console && <any>console.warn) {
console.warn(
Expand Down Expand Up @@ -209,14 +213,18 @@ export class NgForOf<T, U extends NgIterable<T> = NgIterable<T>> implements DoCh
// React on ngForOf changes only once all inputs have been initialized
const value = this._ngForOf;
if (!this._differ && value) {
if (typeof ngDevMode === 'undefined' || ngDevMode) {
if (NG_DEV_MODE) {
try {
// CAUTION: this logic is duplicated for production mode below, as the try-catch
// is only present in development builds.
this._differ = this._differs.find(value).create(this.ngForTrackBy);
} catch {
throw new Error(`Cannot find a differ supporting object '${value}' of type '${
getTypeName(value)}'. NgFor only supports binding to Iterables such as Arrays.`);
let errorMessage = `Cannot find a differ supporting object '${value}' of type '` +
`${getTypeName(value)}'. NgFor only supports binding to Iterables, such as Arrays.`;
if (typeof value === 'object') {
errorMessage += ' Did you mean to use the keyvalue pipe?';
}
throw new RuntimeError(RuntimeErrorCode.NG_FOR_MISSING_DIFFER, errorMessage);
}
} else {
// CAUTION: this logic is duplicated for development mode above, as the try-catch
Expand Down
4 changes: 3 additions & 1 deletion packages/common/src/errors.ts
Expand Up @@ -14,5 +14,7 @@ export const enum RuntimeErrorCode {
// NgSwitch errors
PARENT_NG_SWITCH_NOT_FOUND = 2000,
// Pipe errors
INVALID_PIPE_ARGUMENT = 2100
INVALID_PIPE_ARGUMENT = 2100,
// NgForOf errors
NG_FOR_MISSING_DIFFER = 2200,
}
13 changes: 11 additions & 2 deletions packages/common/test/directives/ng_for_spec.ts
Expand Up @@ -114,13 +114,22 @@ let thisArg: any;
detectChangesAndExpectText('1;2;3;');
}));

it('should throw on non-iterable ref and suggest using an array', waitForAsync(() => {
it('should throw on non-iterable ref', waitForAsync(() => {
fixture = createTestComponent();

getComponent().items = <any>'whaaa';
expect(() => fixture.detectChanges())
.toThrowError(
/Cannot find a differ supporting object 'whaaa' of type 'string'. NgFor only supports binding to Iterables such as Arrays/);
/NG02200: Cannot find a differ supporting object 'whaaa' of type 'string'. NgFor only supports binding to Iterables, such as Arrays./);
}));

it('should throw on non-iterable ref and suggest using an array ', waitForAsync(() => {
fixture = createTestComponent();

getComponent().items = <any>{'stuff': 'whaaa'};
expect(() => fixture.detectChanges())
.toThrowError(
/NG02200: Cannot find a differ supporting object '\[object Object\]' of type 'object'. NgFor only supports binding to Iterables, such as Arrays. Did you mean to use the keyvalue pipe?/);
}));

it('should throw on ref changing to string', waitForAsync(() => {
Expand Down

0 comments on commit 57e8fc0

Please sign in to comment.