fix(ngFor): give more instructive error when binding to non-iterable
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] ```
This commit is contained in:
parent
0898bca939
commit
49527ab495
|
@ -9,11 +9,12 @@ import {
|
||||||
EmbeddedViewRef,
|
EmbeddedViewRef,
|
||||||
TrackByFn
|
TrackByFn
|
||||||
} from 'angular2/core';
|
} from 'angular2/core';
|
||||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
import {isPresent, isBlank, stringify, getTypeNameForDebugging} from 'angular2/src/facade/lang';
|
||||||
import {
|
import {
|
||||||
DefaultIterableDiffer,
|
DefaultIterableDiffer,
|
||||||
CollectionChangeRecord
|
CollectionChangeRecord
|
||||||
} from "../../core/change_detection/differs/default_iterable_differ";
|
} 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
|
* The `NgFor` directive instantiates a template once per item from an iterable. The context for
|
||||||
|
@ -77,7 +78,12 @@ export class NgFor implements DoCheck {
|
||||||
set ngForOf(value: any) {
|
set ngForOf(value: any) {
|
||||||
this._ngForOf = value;
|
this._ngForOf = value;
|
||||||
if (isBlank(this._differ) && isPresent(value)) {
|
if (isBlank(this._differ) && isPresent(value)) {
|
||||||
|
try {
|
||||||
this._differ = this._iterableDiffers.find(value).create(this._cdr, this._ngForTrackBy);
|
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.`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {BaseException} from 'angular2/src/facade/exceptions';
|
||||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||||
import {ChangeDetectorRef} from '../change_detector_ref';
|
import {ChangeDetectorRef} from '../change_detector_ref';
|
||||||
|
@ -86,7 +86,8 @@ export class IterableDiffers {
|
||||||
if (isPresent(factory)) {
|
if (isPresent(factory)) {
|
||||||
return factory;
|
return factory;
|
||||||
} else {
|
} 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)}'`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import 'dart:math' as math;
|
||||||
import 'dart:convert' as convert;
|
import 'dart:convert' as convert;
|
||||||
import 'dart:async' show Future, Zone;
|
import 'dart:async' show Future, Zone;
|
||||||
|
|
||||||
String getTypeNameForDebugging(Type type) => type.toString();
|
String getTypeNameForDebugging(Object type) => type.toString();
|
||||||
|
|
||||||
class Math {
|
class Math {
|
||||||
static final _random = new math.Random();
|
static final _random = new math.Random();
|
||||||
|
|
|
@ -62,7 +62,10 @@ export interface Type extends Function {}
|
||||||
export interface ConcreteType extends Type { new (...args): any; }
|
export interface ConcreteType extends Type { new (...args): any; }
|
||||||
|
|
||||||
export function getTypeNameForDebugging(type: Type): string {
|
export function getTypeNameForDebugging(type: Type): string {
|
||||||
|
if (type['name']) {
|
||||||
return type['name'];
|
return type['name'];
|
||||||
|
}
|
||||||
|
return typeof type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {
|
||||||
} from 'angular2/testing_internal';
|
} from 'angular2/testing_internal';
|
||||||
|
|
||||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||||
|
import {IS_DART} from 'angular2/src/facade/lang';
|
||||||
import {Component, TemplateRef, ContentChild} from 'angular2/core';
|
import {Component, TemplateRef, ContentChild} from 'angular2/core';
|
||||||
import {NgFor} from 'angular2/src/common/directives/ng_for';
|
import {NgFor} from 'angular2/src/common/directives/ng_for';
|
||||||
import {NgIf} from 'angular2/src/common/directives/ng_if';
|
import {NgIf} from 'angular2/src/common/directives/ng_if';
|
||||||
|
@ -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',
|
it('should throw on ref changing to string',
|
||||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||||
tcb.overrideTemplate(TestComponent, TEMPLATE)
|
tcb.overrideTemplate(TestComponent, TEMPLATE)
|
||||||
|
|
Loading…
Reference in New Issue