0
votes

I'm building an Angular app with Node backend. Here's my server.js relevant parts with REST endpoints:

var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var cors = require('cors');
var mysql = require('mysql');

const con = mysql.createConnection({
    host: 'localhost',
    user: 'myusername',
    password: 'mypass',
    database: 'somedb'
});

con.connect((err) => {
    if (err) throw err;
    console.log('Connected!');
});

app.use(bodyParser.json());
app.use(cors());

app.route('/api/levels').get((req, res) => {
    con.query('SELECT * FROM level', (err, rows) => {
        if (err)
            res.send(JSON.stringify({ "status": 500, "error": err, "response": null }));
        else
            res.send(JSON.stringify({ "status": 200, "error": null, "response": rows }));
    });
});
...

This part works, I've tested it via Postman and I get the following response:

{
    "status": 200,
    "error": null,
    "response": [
        {
            "id": 1,
            "title": "A1.1"
        },
        {
            "id": 2,
            "title": "A1.2"
        },
        ... // the rest of the rows
    ]
}

Angular service:

@Injectable()
export class LevelService {

  constructor(private http: HttpClient) {}

  public getAll() {
    return this.http.get('http://localhost:8000/api/levels/');
  }
  ...
}

The service is called in a component:

@Component({
  selector: 'app-level-list',
  templateUrl: './level-list.component.html',
  styleUrls: ['./level-list.component.css']
})
export class LevelListComponent implements OnInit {
  private levels: any[];

  constructor(private levelService: LevelService) { }

  ngOnInit() {
    this.levelService.getAll().subscribe((data: any[]) => this.levels = data);
  }
}

The data is used in the component's template:

<ul class="level-list">
  <li *ngFor="let level of levels">
    <app-level-list-item [level]="level">
    </app-level-list-item>
  </li>
</ul>

And finally it should be displayed on the home page:

<div style="text-align:center">
  <h1>{{ title }}</h1>
</div>
<p>{{introText}}</p>
<app-level-list></app-level-list>

However, when I open the page, there's no data visible, and in the console there's an error:

ERROR Error: Cannot find a differ supporting object '[object Object]' of type 'object'.
NgFor only supports binding to Iterables such as Arrays.

What am I doing wrong?

4
i think your levels is not an array, or you have an error with parsing the response, try to display this.levelsFateh Mohamed
Try this: (data: any[]) => this.levels = data.responseAlexander Poshtaruk

4 Answers

3
votes

It doesn't look like your service response is an array type but rather:

interface ServiceResponse {
    status: number;
    error: any; // not apparent of the type
    response: Level[];
}

I think that you need to change your getAll method to map it so the response is your Level[] instead of ServiceResponse:

public getAll(): Observable<Level[]> {
    return this.http.get<ServiceResponse>('http://localhost:8000/api/levels/')
        .map(res => res.response);
}
2
votes

Http request returns you a Response type, and the actual data you sent is in it's body. To get it, use .json() method and after that you can access your array like property, like guys mentioned above.

this.levelService.getAll().subscribe(data => this.levels = data.json().response);

UPDATE

It comes out that there's no need to use .json() for HttpClient (actually, I was thinking about Http when wrote that) So you can just write

this.levelService.getAll().subscribe(data => this.levels = data['response']);

And the brackets notation seems to be required according to documentation.

1
votes

I got this exception recently. It boiled down to the fact that i was trying to ngFor over something I thought was array of objects but really wasn't. It was a single object or a primative.

I would recommend debugging this in the browser and verifying that levels is actually an array.

0
votes

You need to add a map method to your service and convert the response to JSON. change your Angular Service to the following:

@Injectable()
export class LevelService {

  constructor(private http: HttpClient) {}

  public getAll() {
    return this.http.get('http://localhost:8000/api/levels/')
    .map(res=>res.json());
  }
  ...
}