Solvedangular Misleading error message "Cannot find a differ supporting object '[object Object]'"

This error message gives us no clue as to what is wrong or what to do.

Repro

Misko and I experienced this while refactoring a Component from one using the async pipe to one using an array.

The async pipe version:

    heroes: any;

    // <li *ngFor="#hero of heroes | async">
    this.heroes = this._heroService.getHeroes();

The refactored version:

    // <li *ngFor="#hero of heroes">
    this.heroes = this._heroService.getHeroes()
                      .subscribe(heroes => this.heroes = heroes);

The exception was:

EXCEPTION: Cannot find a differ supporting object '[object Object]' in [heroes  in TohComponent@4:8]

Discussion

Do you see our mistake ... the one that threw the error? We didn't ... for about 5 head-scratching minutes (see below).

The developer has no idea what a "differ" is, what it is that the "differ" can't support, what might cause this, or what to do.

The "[heroes in TohComponent@4:8]" didn't help because line 4 of that component file is an import statement in the TS and nowhere near the template or the function in the transpiled JS. Something called "heroes" appears all over the place.

FWIW, I've received this error several times and I think the cause (bad list) was always the same.


Our mistake: after refactoring we were still assigning an observable to this.heroes.

Recommended solution

Improve error message. Link to problem/solutions page?

51 Answers

✔️Accepted Answer

I just ran into the same issue. I'm not sure if the recommended solution will work for my case.

I'm using an observer to pull a more complicated data structure from an external source (ie an observer on a service that persists the result of a GET request).

The part I'm trying to iterate over is a subset of the external source.

Data:

"vitae": {
  {
    "location": {
      "address": "2445 Torrejon Pl",
      "city": "San Diego",
      "region": "CA",
      "code": "92009",
      "country": "US"
    },
    "social": [
      {
        "label": "",
        "network": "GitHub",
        "user": "evanplaice",
        "url": "https://github.com/evanplaice"
      },
      {
        "label": "",
        "network": "Twitter",
        "user": "@evanplaice",
        "url": "https://twitter.com/evanplaice"
      },
      {
        "label": "",
        "network": "StackOverflow",
        "user": "evanplaice",
        "url": "http://stackoverflow.com/users/290340/evan-plaice"
      },
      {
        "label": "",
        "network": "HackerNews",
        "user": "evanplaice",
        "url": "https://news.ycombinator.com/user?id=EvanPlaice"
      }
    ],
...

The source has more data but the point is, I want to extract the social array and iterate over it in my template.

<ul *ngFor="#profile of vitae.social">
  <li>{{ profile.network }}: <a href="{{ profile.url }}">{{ profile.user }}</a></li>
</ul>

Angular can't seem to recognize that the incoming data is nothing more than an array of objects.
To workaround, I created a hack that just creates a new array from the incoming array.

hack(val) {
  return Array.from(val);
}
<ul *ngFor="#profile of hack(vitae.social)">
  <li>{{ profile.network }}: <a href="{{ profile.url }}">{{ profile.user }}</a></li>
</ul>

The funny thing is, if I log the data before and after the hack it's virtually identical.

Using:

hack(val) {
  console.log('Before:');
  console.log(val);
  val = Array.from(val);
  console.log('After:');
  console.log(val);
  return val;
}

Result:
screen shot 2016-01-13 at 12 40 04 pm

imgres

Other Answers:

@wardbell in your example, you're assigning a Subscription to the this.heroes property, not an Observable. Typescript should catch this, though I agree the error message could be better. The only thing we can really say is "you're not iterating over an array, you should be"?

There's a lot of mixed information going on here, let me see if I can clear it up.

First, pretty much anything that is not a primitive value (string, boolean, number) in Javascript is an Object. This includes Promises, Observables, Arrays, etc - they all are subtypes of Object.

An Iterable in javascript is, in the simplest terms, a "collection" - it's a data structure that allows iteration in an ordered manner. Arrays are the most common example of this. All arrays are iterable, but plain objects are not - importantly, the ecmascript specification does not guarantee the order of items in an Object - whereas an Array does guarantee order.

The IterableDiffer you see in the error message is an angular mechanism that compares two collections (iterables) and returns:

  • objects added to the collection
  • objects removed from the collection
  • objects that have changed their position in the collection

We do this to enable, for example - animations - we can tell when an item has moved from position 3 to position 0 in an array, so it can be appropriately animated:

//psuedo code!
//given two arrays:
let a = [1,2,3];
let b = [3,2,4];
iterableDiffer.create(a).diff(b) // 1 is removed, 4 is added, 2 and 3 are moved

Part of the reason (the other is performance) we don't support objects in ngFor/differs is that this mechanism doesn't work for objects - consider:

var foo = {};
foo.a = 1;
foo.b = 2;
Object.keys(foo) // [ 'a', 'b' ]
delete foo.a;
foo.a = 1;
Object.keys(foo) // [ 'b', 'a' ]

Objects have no concept of order - typically the keys are returned in insertion order (as above) - and so ngFor'ing over them doesn't make sense!

You could, of course, implement your own objectToKeys pipe, but then you run into ordering problems, and thus you have to maintain the sort order yourself somehow - there are a lot of edge cases here that might impact performance so we leave it up to the developer.

Regarding @evanplaice's example above - it's important to understand how javascript handles values and references:

//imagine you have an object of objects:
this.foos = {
  a: { name: 'fooA'}
  b: { name: 'fooA'}
}
//turn this into an array
this.listOfFoos = Object.keys(this.foos).map(key => this.foos[key]) // [{name: 'fooA'}, {name: 'fooB'}];

//objects are copied by reference:
this.foos.a === this.listOfFoos[0] //true -> they are the same reference
this.foos.a.name = 'fooA!'
console.log(this.listOfFoos[0]) // { name: 'fooA!' }

So simply moving an object from one structure to another does not "break" the reference - they would continue to update. See plunker: https://plnkr.co/edit/34dPSfL0bIDfGkdySExX?p=preview

The same does not hold true for primitive values:

//imagine you have an object of numbers:
this.foos = {
  a: 1
  b: 2
}
//turn this into an array
this.listOfFoos = Object.keys(this.foos).map(key => this.foos[key]) // [1, 2];

//values are simply copied
this.foos.a === this.listOfFoos[0] //true -> they are the same value
this.foos.a = 3 //change the value
console.log(this.listOfFoos[0]) //still 1

So with primitive values, they are "disconnected"

All of the above deals with simple synchronous values. The second round of confusion here has to do with Promises and Observables. You can deal with async values in Angular in the following ways:

By simply .then() or .subscribe() to a Promise or Observable, respectively, and assigning the output value:

somePromiseThatGetsAnArrayOfFoos().then(foos => {
  this.foos = foos;
});

someObservableOfArrayOfBars.subscribe(bars => {
  this.bars = bars; 
});

To render these, you can simply ngFor over them:

<ul>
  <li *ngFor="#foo of foos">{{foo.name}}</li>
</ul>
<ul>
  <li *ngFor="#bar of bars">{{bar.name}}</li>
</ul>

What you cannot do here is *ngFor over a sub-property of an async delivered value - because when the view is initially rendered, the parent property is undefined.

//this.foo is undefined here!
somePromiseThatGetsAnObjectWithNestedBars().then(foo => {
 //  { bars: [ 1, 2, 3] }
  this.foo = foo;
});

So doing

<ul>
  <li *ngFor="#bar of foo.bars">{{bar}}</li>
</ul>

Will throw an error: EXCEPTION: TypeError: Cannot read property 'bars' of undefined in [foo.bars]
because foo is undefined on initial render.

This is exactly the same behavior you see in regular Javascript:

var foo;

somePromise.then(fooData => {
  foo = fooData;
});
console.log(foo.bars) //Uncaught TypeError: Cannot read property 'bars' of undefined(…)

The simple fix is to use the ? operator:

<ul>
  <li *ngFor="#bar of foo?.bars">{{bar}}</li>
</ul>

The other way of handing async values is to use the async pipe:

this.foos = somePromiseThatReturnsFoos();
this.bars = someObservableOfBars;
<ul>
  <li *ngFor="#foo of foos | async">{{foo.name}}</li>
</ul>
<ul>
  <li *ngFor="#bar of bars | async">{{bar.name}}</li>
</ul>

Note here that we're assigning the Promise / Observable to the class, not the result of .then() / .subscribe()

To handle nested values, you can use the same ? operator:

<ul>
  <li *ngFor="#bar of (foo | async)?.bars">{{bar}}</li>
</ul>

The same rules apply here with copy-by-value and copy-by-reference - so you could, in theory, store the returned objects in a service, return them in a promise, and two-way binding will work, as long as you maintain the references.

Summary:

  • collections of things should almost always be kept in arrays if order is important.
  • be sure to understand the difference between assigning an array to this and assigning a Promise or Observable of values to this
  • Databinding works fine as long as you aware of the semantics of javascript references.
  • Don't attempt to access properties of undefined values that may be delivered at a later time.

Here's the hack (endearingly named DerpPipe) that can be used to workaround the issue.

import { Pipe } from 'angular2/core';

/*
  # Description:

  Repackages an array subset as a new array.

  **Reasoning:**

  Angular2's change checker freaks out when you ngFor an array that's a subset
    of a larger data structure.

  # Usage:
  ``
  <div *ng-for="#value of arrayOfObjects | derp"> </div>
  ``
*/
@Pipe({ name: 'derp' })
export class DerpPipe {
  transform (value, args) {
    return Array.from(value);
  }
}

So, I think that still assigning an observable to this.heroes after the refactor, is just one of those dumb things we developers sometimes run into. I don't think there's anything that Angular can help with here.

I DO agree, that this error message is very unhelpful as @wardbell pointed out. It's not clear what a differ is, it's not clear what the differ can't support. We should definitely improve this error message with something more useful.

I'm sorry for a probably uninformed comment, I'm very new to Angular 2. And I just came over this error with the code from the official Angular 2 tutorial that threw me a wonderful EXCEPTION: Cannot find a differ supporting object '[object Object]' in [heroes in AppComponent@4:16] (!!).

...but aren't you talking about solving this at the wrong level? Should'n the actual mycrosyntax #item of items be made "smart enough" to just be able to iterate over "almost anything" (like be smart enough to handle Array, Promise<Array>, Object, Promise<Object> etc.) automagically even at the cost of maybe increasing rendering time by even ~20% or stmh? Should a developer actually need to care what he/she is iterating over when writing a template?


EDIT+: my bad, I actually had an error in my code. But this is a very confusing error message indeed! (And I still think that ngFor is the thing that should "behave smarter" here and try and to what "probably is the right thing" regardless of what's given to it...)

Related Issues:

608
angular Angular5.x lazyLoad problem, undefined is not a function
For others that find this issue via Google as i did: I had the same problem when trying to lazy load...
348
angular Cyclic dependency error with HttpInterceptor
I resolved simply not setting authService in constructor but getting in the intercept function. ...
277
angular Uncaught Error: Can't resolve all parameters for ...
You are missing an @Injectable() annotation on your ApiService Support requests like these should li...
266
angular Force reload/refresh current route with RouteReuseStrategy
Hi If you really need to trick the Router into reloading the component on each routerLink click ...
260
angular Misleading error message "Cannot find a differ supporting object '[object Object]'"
I just ran into the same issue I'm not sure if the recommended solution will work for my case ...
224
angular update 2 to 4 has problem [ts] Property 'map' does not exist on type 'Observable<Response>'.
I met the same problem with the angular cli 6.0.0 and rxjs 6.1.0 And I solved the problem by replaci...
170
angular Angular2 AoT Compiler Errors
pls try /cc @chuckjaz When I try to compile my project with ngc it throws the below error: Error: Er...
152
angular HttpClient.delete() cannot handle a body in its request
It would be great to have body param in .delete() We just migrated our project to HttpClient and for...
140
angular Http - Observable completed function not firing
Third callback haven't been called when error occures ES6 promises hasn't method finally only then a...
133
angular Using multiple components in different modules causing "Type X is part of the declarations of 2 modules" error
as @brandonroberts saids create a shared module like this: then use the SharedModule like this.. ...
112
angular Unsupported platform for fsevents@1.0.14: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
@DzmitryShylovich did you edit package.json only? if npm-shrinkwrap.json is still there please remov...
98
angular Angular v5 ngc compiler: Error encountered in metadata generated for exported symbol 'Subscription'
Got this problem I'm submitting a.. Current behavior Building an Angulary library using 5.0.0-beta.4...
98
angular 404 on route refresh in angular 4
Got it working Just adding .htaccess in root I'm submitting a.. Current behavior I created a new com...
97
angular routerLinkActive not updating when routerLink changed
I have a hack that seems to work After looking at the source code it looks like this.update() is als...
97
angular No provider for HttpClient!
If you are using angular v5 version import HttpClientModule in your app.module.ts after HttpModule T...
81
angular ɵDomAnimationEngine and ɵNoopAnimationEngine module missing in animations 4.2.1
@dubedoy I installed @angular/animations@4.1.3 and it worked again. I'm submitting a .. ...
80
angular Error: Runtime compiler is not loaded in angular6 --prod mode
Do not and i repeat do not import your feature modules in app module and also when addressing featur...
79
angular appending header in HttpHeaders from '@angular/common/http' doesn't work
@trotyl I didn't understand your comment I'm submitting a.. ...
77
angular Can't bind to 'formGroup' since it isn't a known property of 'form'
did you import ReactiveFormsModule? I'm submitting a .. ...
73
angular AOT Compiler requires public properties, while non-AOT allows private properties
@aluanhaddad you have a big misunderstanding in here There is no subset of Typescript in here No one...
73
angular [Bug] angular/elements: Failed to construct 'HTMLElement': Please use the 'new' operator
Hi I have solved this issue by changing the target:es5 in the tsconfig.json to target:es2015 these i...
70
angular Cannot run angular 2+ from file:/// - looks like 'base href="/"' is the issue
Thanks @Markovy @audrummer15 I got it working completely in a fairly complex angular 2 app with mult...
69
angular HttpClient fails to parse an empty 200 response in IE11
For my error I was able to fix the problem by setting the responseType: to 'text' in the options ...
66
angular Function calls are not supported in decorators when fullTemplateTypeCheck is not specified and @dynamic has no effect
Regarding Ward's repro: @wardbell The build will succeed / fail depending on the combination of angu...
62
angular error TS2451: Cannot redeclare block-scoped variable 'ngDevMode'
had to add this line in the main tsconfig I'm submitting a.. ...
62
angular Problem with ngFor
Wouldn't [(ngModel)]=testItems[i] do the trick? I think that the error is saying that you can assign...
61
angular Issue with importing Observable from rxjs/Rx (import-blacklisted)
You shouldn't import from 'rxjs' or 'rxjs/Rx' since either import will import the whole of rxjs whic...
53
angular [RC5]: Minified bundle breaks
@robertoforlani Hopefully someone will have time to write a comprehensive explanation soon In the me...
53
angular router-outlet is appending rather than replacing when using BrowserAnimationsModule
Trying this solved the problem for me: this.zone.run(() => { this.router.navigate(['/main']); }); Re...
52
angular Lazy loaded module in named outlet throws error
We have this Where proxy route component is simply [x] bug report [ ] feature request [ ] support re...
52
angular IVY Error NG6002: Appears in the NgModule.imports of AppModule, but could not be resolved to an NgModule class
Not sure this will provide anyone relief or assist with figuring out what the root cause is but clea...
51
angular How to run angular 2 application on apache hosting server
Sorry to rock the boat I hope this doesn't attract more questions I'm only going to comment once :) ...
51
angular Support adding rel=canonical link tags using an included service
Eventually there will be some DocumentService part of Core that will handle both Meta/Link elements ...
50
angular Concept of Angular (ngZone + ChangeDetection) better than concept React, Vue (Virtual DOM)?
Concept of Angular (ngZone + ChangeDetection) better than concept React Vue (Virtual DOM)? If you ca...
50
angular I'd like to be able to use ngModel without specifying a name
Thank you all for the great feedback - very helpful! Here's how we are thinking about it: In the cas...
49
angular Model values not trimming automatically in angular 2
@laskoviymishka White space it already something If you are a programmer and think globally - yes ...
46
angular HttpClient - HttpErrorResponse not json but blob
I created this interceptor as a temporary solution until this one is fixed: I'm submitting a.. ...
45
angular Router's ActivatedRoute data returns empty {} if module is lazy
data is available only with this hell-like construction: And this is if you have children: ...
44
angular Misleading errormessage when using HostBinding with @animation trigger but no defined animations
The error message is not fine The error message says you're importing BrowserAnimationsModule incorr...
42
angular Memory leak when FormControlName created/destroyed few times
This issue has been around for nearly 3 years now (I usually don't like to start a message this way ...
41
angular Remove System.import() usage in favor of import()
I use a parser rule in my webpack configuration to disable the warning: https://webpack.js.org/confi...
41
angular Async event subscriber not updating UI after async call
Hi! The issue is that the async call result is outside ngZone thus not triggering the UI update You ...
40
angular Using router.navigate to navigate to another component does not invoke the onInit method
I have the same issue Angular is running in a Cordova app for iOS I tried the router-version 4.1.3 (...
37
angular Router: AoT compilation fails when using a function with loadChildren
Calling functions or calling new is not supported in metadata when using AoT This includes things li...
35
angular Provide a mock service using TestBed
I was having this issue as well however I noticed that my @component metadata still had the provider...
35
angular Angular2 download excel file from Web API, file is corrupt
@healkar01 I had the same issue and I resolved using native angular2 http request in this way: Backe...
35
angular 4.0.0-rc.6 [platform-server] - Cannot find module '@angular/animations/browser'. & other errors
(Just incase others find it) Make sure @angular/animations is installed as a dependency and the erro...
35
angular HttpClient mapping to typescript types not working
I agree with all the previous comments I find the syntax misleading widget.service.zip widget.servic...