Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular2 ngFor: What am I doing wrong?

Tags:

angular

ngfor

I'm having difficulty overcoming the "Cannot find a differ supporting object '[object Object]' of type 'object'" error that seems to be really common with Angular2, and I'm hoping someone has encountered something similar.

Here's the (anonymized) JSON coming from my service, which is really simple:

[
    {
        "item_id": 1,
        "item_type": 2,
        "item_name": "Item 1",
        "item_description": "First item"
    },
    {
        "item_id": 2,
        "item_type": 4,
        "item_name": "Item 2",
        "item_description": "Second item"
    }
]

And here's the contents of my class, service, and component that describes these objects:

// item.ts
export class Item {
    item_id: number;
    item_type: number;
    item_name: string;
    item_description: string;
}

//item.service.ts snippet
getItems(): Promise<Item[]> {
    return this.http.get('http://apiurl', { withCredentials: true })
    .toPromise()
    .then((response) => {
        let body = response.json();
        return body as Item[];
    })
    .catch(this.handleError);
}

//item.component.ts snippet
items: Item[];

getItems(): void { // Function that calls the item service
    this.itemService
    .getItems()
    .then((items) => {
        console.log(items); // I use this to verify that I'm getting an array.
        this.items = items;
    });
}

And finally, the ngFor component:

<ul>
    <li *ngFor="let item of items">
        <i class="fa fa-database"></i> {{item.item_name}}
    </li>
</ul>

I'm not seeing anything wrong with any part of this. The retrieved data definitely does reach the item component, which means my imports are correct, and what shows up in my console.log is definitely an array, with the __proto__:Array[0] property and everything. It even looks identical to what gets output if I console log the Angular tutorial Heroes app. Yet it simply will not iterate over the array, insisting that it's an object.

What am I doing wrong? Is ngFor just broken?

Edit Here is the complete (anonymized) class, with unrelated bits removed:

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Headers, Response, Http, RequestOptions } from '@angular/http';
import { Item } from '../../classes/item/item';
import { ItemService } from '../../services/item/item.service';

@Component({
    moduleId: module.id,
    selector: 'my-items',
    templateUrl: 'items.component.html'
})
export class ItemComponent implements OnInit {
    items: Item[] = [];

    constructor(
    private router: Router,
    private itemService: ItemService
    ) { }

    getItems(): void {
        console.log(this.items);
        this.itemService
        .getItems()
        .then((items) => {
            console.log(items);
            this.items = Array.from(items);
        });
    }

    ngOnInit() {
        this.getItems();
    }
}

Edit 2:

I got it! And I think it might be a bug in Angular2. Above, I sanitized my code by using a generic variable name called "items." But in the real, production code, the variable is called "entities." And for the whole time, I've had it named like that. On a whim, I changed the name of the variable to "testentities," and it worked!

So just to make sure, I tested with multiple variations, and it worked every time. Then I changed it back to "entities," and the error reappeared. It seems to be a reserved variable of some kind.

I'll rigorously test this out, and if it is consistently reproducible, I'll report it on the bug tracker.

like image 295
Damon Kaswell Avatar asked Nov 08 '22 07:11

Damon Kaswell


1 Answers

Give this a try

item.service.ts snippet

getItems(): Promise<Item[]> {
    return this.http.get('http://apiurl', { withCredentials: true })
    .toPromise()
    .then((response) => {
        let body = response.json();
        return body;
    })
    .catch(this.handleError);
}

item.component.ts snippet

items: Item[] = []; // For whatever reason it thinks this is an Object

getItems(): void { // Function that calls the item service
    //this.items = Array.from(this.items);
    this.itemService.getItems().then(items => this.items = items);
}

[UPDATE]

Alight, you have exhausted my debugging resources, so I will take it to the basics,

Angular 2 HTTP Client is where they show you how to make a GET request. Then HERE is the Promise based setup.

So going off of that this is what yours should look like.

item.service.ts snippet

getItems(): Promise<Item[]> {
    return this.http.get('http://apiurl', { withCredentials: true })
    .toPromise()
    .then(this.extractData)
    .catch(this.handleError);
}
private extractData(res: Response) {
  let body = res.json();
  return body || { };
}
private handleError (error: any) {
  // In a real world app, we might use a remote logging infrastructure
  // We'd also dig deeper into the error to get a better message
  let errMsg = (error.message) ? error.message :
    error.status ? `${error.status} - ${error.statusText}` : 'Server error';
  console.error(errMsg); // log to console instead
  return Promise.reject(errMsg);
}

item.component.ts

items: Item[] = [];

getItems(): void {
  this.itemService.getItems().then(items => this.items = items);
}


UPDATE #2

It sounds like the OP figured out his problem and it had to do with the name of one of the variables, it would appear that a variable named entites could cause Angular 2 to break.

"But in the real, production code, the variable is called "entities." And for the whole time, I've had it named like that. On a whim, I changed the name of the variable to "testentities," and it worked!

So just to make sure, I tested with multiple variations, and it worked every time. Then I changed it back to "entities," and the error reappeared."

entities: any{}; 
like image 136
Logan H Avatar answered Nov 28 '22 08:11

Logan H