Reputation: 611
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?
Upvotes: 0
Views: 431
Reputation: 186
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.
Upvotes: 2
Reputation: 543
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());
}
...
}
Upvotes: 0
Reputation: 7153
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.
Upvotes: 1
Reputation: 8470
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);
}
Upvotes: 3