Skip to content

Commit

Permalink
fix(ngFor): give more instructive error when binding to non-iterable
Browse files Browse the repository at this point in the history
Before, you'd get an error like:

```
EXCEPTION: Cannot find a differ supporting object β€˜[object Object]’ in [users in UsersCmp@2:14]
```

Now, you get:

```
EXCEPTION: Cannot find a differ supporting object β€˜[object Object]’ of type 'Object'. Did you mean to bind ngFor to an Array? in [users in UsersCmp@2:14]
```
  • Loading branch information
btford committed Mar 24, 2016
1 parent 0898bca commit 49527ab
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 6 deletions.
10 changes: 8 additions & 2 deletions modules/angular2/src/common/directives/ng_for.ts
Expand Up @@ -9,11 +9,12 @@ import {
EmbeddedViewRef,
TrackByFn
} from 'angular2/core';
import {isPresent, isBlank} from 'angular2/src/facade/lang';
import {isPresent, isBlank, stringify, getTypeNameForDebugging} from 'angular2/src/facade/lang';
import {
DefaultIterableDiffer,
CollectionChangeRecord
} from "../../core/change_detection/differs/default_iterable_differ";
import {BaseException} from "../../facade/exceptions";

/**
* The `NgFor` directive instantiates a template once per item from an iterable. The context for
Expand Down Expand Up @@ -77,7 +78,12 @@ export class NgFor implements DoCheck {
set ngForOf(value: any) {
this._ngForOf = value;
if (isBlank(this._differ) && isPresent(value)) {
this._differ = this._iterableDiffers.find(value).create(this._cdr, this._ngForTrackBy);
try {
this._differ = this._iterableDiffers.find(value).create(this._cdr, this._ngForTrackBy);
} catch (e) {
throw new BaseException(
`Cannot find a differ supporting object '${value}' of type '${getTypeNameForDebugging(value)}'. NgFor only supports binding to Iterables such as Arrays.`);
}
}
}

Expand Down
@@ -1,4 +1,4 @@
import {isBlank, isPresent, CONST} from 'angular2/src/facade/lang';
import {isBlank, isPresent, CONST, getTypeNameForDebugging} from 'angular2/src/facade/lang';
import {BaseException} from 'angular2/src/facade/exceptions';
import {ListWrapper} from 'angular2/src/facade/collection';
import {ChangeDetectorRef} from '../change_detector_ref';
Expand Down Expand Up @@ -86,7 +86,8 @@ export class IterableDiffers {
if (isPresent(factory)) {
return factory;
} else {
throw new BaseException(`Cannot find a differ supporting object '${iterable}'`);
throw new BaseException(
`Cannot find a differ supporting object '${iterable}' of type '${getTypeNameForDebugging(iterable)}'`);
}
}
}
2 changes: 1 addition & 1 deletion modules/angular2/src/facade/lang.dart
Expand Up @@ -5,7 +5,7 @@ import 'dart:math' as math;
import 'dart:convert' as convert;
import 'dart:async' show Future, Zone;

String getTypeNameForDebugging(Type type) => type.toString();
String getTypeNameForDebugging(Object type) => type.toString();

class Math {
static final _random = new math.Random();
Expand Down
5 changes: 4 additions & 1 deletion modules/angular2/src/facade/lang.ts
Expand Up @@ -62,7 +62,10 @@ export interface Type extends Function {}
export interface ConcreteType extends Type { new (...args): any; }

export function getTypeNameForDebugging(type: Type): string {
return type['name'];
if (type['name']) {
return type['name'];
}
return typeof type;
}


Expand Down
19 changes: 19 additions & 0 deletions modules/angular2/test/common/directives/ng_for_spec.ts
Expand Up @@ -14,6 +14,7 @@ import {
} from 'angular2/testing_internal';

import {ListWrapper} from 'angular2/src/facade/collection';
import {IS_DART} from 'angular2/src/facade/lang';
import {Component, TemplateRef, ContentChild} from 'angular2/core';
import {NgFor} from 'angular2/src/common/directives/ng_for';
import {NgIf} from 'angular2/src/common/directives/ng_if';
Expand Down Expand Up @@ -158,6 +159,24 @@ export function main() {
});
}));

if (!IS_DART) {
it('should throw on non-iterable ref and suggest using an array',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
tcb.overrideTemplate(TestComponent, TEMPLATE)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.items = 'whaaa';
try {
fixture.detectChanges()
} catch (e) {
expect(e.message).toContain(
`Cannot find a differ supporting object 'whaaa' of type 'string'. NgFor only supports binding to Iterables such as Arrays.`);
async.done();
}
});
}));
}

it('should throw on ref changing to string',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
tcb.overrideTemplate(TestComponent, TEMPLATE)
Expand Down

0 comments on commit 49527ab

Please sign in to comment.