Tony B. Tony B. - 5 months ago 842
JSON Question

JSON Data not being displayed in Angular 2 component

I new to Spring MVC and Angular 2. I have been working on building a web application as a side project. I have been able to get pretty far as a Spring MVC app but I wanted to migrate to an Angular 2 front end. I have followed the quick start procedures on the Angular.io and seem to be doing okay. The next step is to get the angular service to retrieve data from my Spring MVC REST controller.

I have been able to successfully send a GET to the controller to retrieve a list of users and the Controller sends back an application/json object. The problem is that the users are not showing up in the browser. I am not seeing any errors in the lite-server window and I am not sure how to troubleshoot why the data is not getting displayed. Can you help find my issue?

UserComponent.ts:

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router } from '@angular/router';

import { User } from './user';
import { UserService } from './user.service';
import { Role } from '../roles/role';

@Component({

templateUrl: '/app/users/user.component.html',
providers: [ UserService]
})
export class UserComponent implements OnInit {
errorMessage: string;
users: User[];
mode = 'Observable';
constructor (private userService: UserService) {}

ngOnInit() { this.getUsers(); }

getUsers() {
this.userService.getUsers()
.subscribe(
users => this.users = users,
error => this.errorMessage = <any>error);
}

}


UserComponent.html:

<h2>My User Component Page</h2>

<h3>Users:</h3>
<ul>
<li *ngFor="let user of user">
{{user.username}}
</li>
</ul>
<div class="error" *ngIf="errorMessage">{{errorMessage}}</div>


User.service.ts:

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { User } from './user';
import { Observable } from 'rxjs/Observable';
@Injectable()
export class UserService {
constructor (private http: Http) {}
private usersUrl = 'http://serverIP/labtool/users'; // URL to web API
getUsers (): Observable<User[]> {
return this.http.get(this.usersUrl)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
let body = res.json();
return body.data || { };
}
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 Observable.throw(errMsg);
}
}


Wireshark Screenshot showing the returned array:

User Class:

import { Role } from '../roles/role'
export class User {
constructor(
public id: number,
public firstname: string,
public lastname: string,
public username: string,
public password: string,
public email: string,
public enabled: boolean,
public roleid: Role) { }
}


Update:

I was able to use the Chrome developer tools to show in the browser that the data is getting there, but the data is not being rendered in the component page. Below is the data from the Network Headers in Chrome. Is there a problem with the encoding differences with the Request and Answer?

The data is being returned from a Spring MVC REST controller that is hosted on a Tomcat 8 server in Eclipse. The Angular 2 front end is running off a lite-server and is edited with VS Code. I haven't yet figured out how to merge the two projects together. That will come later.

Additionally, if I copy the Response Data from the Chrome browser and use it in the InMemoryBackendService and InMemoryDataService (like the Heroes Tutorial on the angular.io web site) the data does show up in the component. Formatting seems to be fine, but not sure why the data from the REST controller is not getting populated in the component page.

General:
Request URL:http://<server IP>/labtool/users
Request Method:GET
Status Code:200 OK
Remote Address:10.133.137.55:80


Response Headers:
Access-Control-Allow-Origin:*
Cache-Control:no-cache, no-store, max-age=0, must-revalidate
Content-Type:application/json;charset=UTF-8
Date:Thu, 07 Jul 2016 10:27:01 GMT
Expires:0
Pragma:no-cache
Server:Apache-Coyote/1.1
Transfer-Encoding:chunked
X-Content-Type-Options:nosniff
X-Frame-Options:DENY
X-XSS-Protection:1; mode=block


Request Headers:
Accept:*/*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-US,en;q=0.8
Cache-Control:no-cache
Connection:keep-alive
Host:<server IP>
Origin:http://localhost:3000
Pragma:no-cache
Referer:http://localhost:3000/users
User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36


Screenshot of Chrome

I am now getting this exception in the console. I have seen other posts suggesting use of a Pipe to convert the JSON object to an array for use in the *ngFor template. Why doesn't the getUsers() procedure in the user.service.ts take care of it already? Am I missing something in my procedure or do I have to use a Pipe?

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

Answer

I found the answer. I need to send a thank you to George Saadeh and his blog. That is where I found the answer. Falafel Software Blog http://blog.falafel.com/working-http-introduction-angular2

There were two issues. The first was that my user.ts file was configured as a class and not as an interface. Here is the final user.ts file

export interface User {
     id: number;
     firstname: string;
     lastname: string;
     username: string;
     password: string;
     email: string;
     enabled: boolean;
     roleid:  {id: number, role: string}
}

The second was that the function in the user.service.ts file, getUsers() needed to map the response to the array User[]

getUsers(): Observable<User[]> {
  return this.http.get(this.usersUrl)
    .map((response: Response) => <User[]>response.json())
    .catch(this.handleError);
}