Using *ngFor with a JSON object in Angular 2 - json

I'm trying to implement a decoupled wordpress solution and I'm having a bit of confusion displaying the JSON object properties in my template. I'm able to return JSON objects for the WP API but not sure how to handle them. The only way I can get a property to display it's value in a template is if I add a [0] to the interpolated property, which won't work in an ngFor loop. I've read the solution by #Thierry here access key and value of object using *ngFor
but this doesn't seem to be how Google handles the Tour of Heroes app http://plnkr.co/edit/WkY2YE54ShYZfzJLSkMX?p=preview
Google uses this data set:
{
"data": [
{ "id": "1", "name": "Windstorm" },
{ "id": "2", "name": "Bombasto" },
{ "id": "3", "name": "Magneta" },
{ "id": "4", "name": "Tornado" }
]
}
which looks like a JSON object to me, so how is the app able to handle something like this:
<ul>
<li *ngFor="let hero of heroes">
{{hero.name}}
</li>
</ul>
I'm just unclear if there's been a change in RC5 that allows iteration over an object, or do I still need to transform this somehow. I'm very new to Angular and could use a little guidance on this matter. Thanks!!
An update based on the comments, if I want to transform an api request like http://localhost:8888/wp-json/wp/v2/posts, what's the best method for that? My return code would look something like:
[
{
"id": 4,
"date": "2016-08-09T00:09:55",
"date_gmt": "2016-08-09T00:09:55",
"guid": {
"rendered": “http://localhost:8888/?p=4"
},
"modified": "2016-08-09T00:11:05",
"modified_gmt": "2016-08-09T00:11:05",
"slug": “wp-api-test”,
"type": "post",
"link": "http://localhost:8888/2016/08/wp-api-test”,
"title": {
"rendered": "testing the wp api"
},
"content": {
"rendered": "<p>loreum ipsum</p>\n"
},
"excerpt": {
"rendered": "<p>loreum ipsum</p>\n"
},
"author": 1,
"featured_media": 0,
"comment_status": "open",
"ping_status": "open",
"sticky": false,
"format": "standard",
"categories": [
1
],
}
]

Without writing all the code for you, there is no short answer to what you are asking, but here are some tips:
You have to take the JSON response you are getting back and create either an interface or class in TypeScript so when you perform the POST, Angular 2 can take the JSON and convert it to an object using your classes.
JSON to TS generator: http://json2ts.com/
Ninja Tips 2 - Make your JSON typed with TypeScript - http://blog.ninja-squad.com/2016/03/15/ninja-tips-2-type-your-json-with-typescript/
TypeScript Json Mapper - http://cloudmark.github.io/Json-Mapping/

How are you defining your services?
Here's an example:
export interface IPost {
id: number;
date: Date;
date_gmt: Date;
... // rest of your defintion
}
#Injectable()
export class BlogPostService {
private http: Http;
constructor(http: Http) {
this.http = http;
}
private apiUrl: string = 'api/blog/posts.json';
getPosts(): Observable<IPost[]> {
return (this.http.get(this.apiUrl)
.map((response: Response) => <IPost[]>response.json())
.do(data => console.log('All: ' + JSON.stringify(data)))
.catch(this.handleError)) as any;
}
private handleError(error: Response) {
console.error(error);
return Observable.throw(error.json().error || "Server error");
}
}
This line should transform your json object to array[] of IPosts
.map((response: Response) => <IPost[]>response.json())
Hope that helps.

Thanks so much for the answers. They helped lead me to the solution. I was able to solve this rather simply by creating a posts array and making it equal to the Observable return. The component looks like this:
posts: {};
constructor (private _wpService: WpApiService) {}
ngOnInit() { this.getPosts() }
getPosts() {
this._wpService.getPosts()
.subscribe(
data => {
this.posts = data;
});
}
The template looks like:
<li *ngFor="let post of posts">
<h3>{{post.title.rendered}}</h3>
</li>

Just replace {{hero.name}} by {{hero.data.name}}
The best way to handle the JSON object by creating an interface class to access the data easily

Related

How to use Rest Template to step into a JSON object?

I have learned how to use a rest template to access a JSON array like this:
[
{
"ID": "0ae6496f-bb0b-4ebd-a094-ca766e82f3e7",
"Confirmed": 0,
}
{
"ID": "e010ced5-c7cb-4090-a7ed-206f4c482a5b",
"Confirmed": 0,
}
]
I accessed the Confirmed for example with
public Model[] getModel() {
ResponseEntity<Model[]> response = restTemplate.getForEntity(apiUrl, Model[].class);
return response.getBody();
}
but now I have to access data in another Json from another API. The data looks like this
{
"prefixes": [
{
"region": "ap-northeast-2",
"service": "AMAZON",
},
{
"region": "eu-west-3",
"service": "AMAZON",
}
]
}
How can I access region or service inside, and what would be the proper name for this?
The first is a JSON array, the second a JSON object?
The first API is simply
https://example.com
Whereas the second is
https://example.com/data.json.
You have to create the POJO for return type:
List<RegionServiceObject> items;
Where RegionServiceObject looks:
public class RegionServiceObject {
private String region;
private String service;
// constructors, getters/setters, toString()....
}
The way of deseariliseing to object is similar to which you already wrote:
RegionServiceObject[] items = restTemplate.getForObject(url, RegionServiceObject[].class);
and access for specific item will be as usual for specific item:
for (RegionServiceObject item : items) {
item.getRegion();
item.getService();
// use them here
}

Angular calling API with observable is not displaying data

I have implemented an angular 5 application to bring data from a Web API. I am using an observable to get the json. The Json coming from the API looks like this:
{
"Job": [
{
"Title": "Solution Architect",
"Summary": "Solution Architect",
"Salary": {
"MinValue": "100",
"MaxValue": "100",
"Text": "",
"Period": "HourlyRate"
},
"Reference": "234483_1",
},
{
"Title": "Senior Business Analyst – eCommerce ",
"Summary": "Senior Business Analyst...",
"Salary": {
"MinValue": "80",
"MaxValue": "100",
"Text": "",
"Period": "HourlyRate"
},
"Reference": "234874_1",
}
],
"Advertiser": "Resourcing",
"Source": "Wiz"
}
I have created a service looks as follows:
....
#Injectable()
export class AdvDataService {
private _getAddsUrl = 'https://xx.xxxx.com/v1/adverts/www111';
constructor(private _http: Http) { }
getAdvForClient(): Observable<IAdv> {
return this._http.get(this._getAddsUrl)
.map((response: Response) => <IAdv>response.json())
.do(data => console.log('All:' + data))
.catch(this.handleError);
}
The component looks like that:
....
export class HomeComponent implements OnInit {
constructor(private _sdvDataService: AdvDataService) { }
private loadAdverts: IAdv;
private errorMesage: string;
ngOnInit() {
this.errorMesage = "";
this._sdvDataService.getAdvForClient()
.subscribe(adv => {
this.loadAdverts = adv;
console.log ("here:" + this.loadAdverts)
}, error => {
this.errorMesage = <any>error;
});
}
}
I have a simple interface:
export interface IAdv {
Advertiser: string,
Source:string
}
and my html:
<div *ngIf="loadAdverts" >
<div class="row justify-content-center">
<div class="card" style="width: 20rem;">
<div class="card-body">
<h4 class="card-title">here: {{loadAdverts.Advertiser}}</h4>
<h6 class="card-subtitle mb-2 text-muted">{{loadAdverts.source}} </h6>
<p class="card-text">{{loadAdverts.source}}</p>
Card link
Another link
</div>
</div>
</div>
I can see the json displayed in the console from both console.log in the component and in the service but any data is displayed in the view.
I would appreciate any help?
Update: It seems the json coming from the server is a json string. So it is not being parsed in the observable. I had to use json.parse in the suscriber. How can I parse that json string to a json object automatically with the suscriber?
Your loadAdverts is a private variable and thus not accessible in the template.
Either make it public like this.
public loadAdverts:IAdv;
or use getters and setters.
private _loadAdverts:IAdv;
....
get loadAdverts(): IAdv {
return this._loadAdverts;
}
set loadAdverts(_ad: IAdv) {
this._loadAdverts = _ad;
}
....
As we can see, you are receiving an Object Job containing an array, so you should extract that array from the object:
getAdvForClient(): Observable<IAdv> {
return this._http.get(this._getAddsUrl)
.map((response: Response) => <IAdv>response.json().Job) // here!
.do(data => console.log('All:' + data))
.catch(this.handleError);
}
also, since you are getting an array, you would want to iterate that in your template:
<div *ngFor="let adv of loadAdverts">
<p>{{adv.Source}}</p>
<!-- more code here -->
</div>
also notice that this is case sensitive, in your template you used source as the property name instead of Source.

How to get json value from Angular2 app

I need to get values from a JSON file which is served from fake-json-server.
To be precise, I need an exact value, e.g. I need to get all "type": "values" where group is Air.
I'm using Angular2 with TypeScript and here is a part of the code where I'm doing a get request in the TransformerService file:
getVehicleGroups(groupName: string) {
return this.http.get(`${this.apiUrl}/vehicleTypes?group=${groupName}`)
.map((res: Response) => res.json() as VehicleTypes[]).catch(this.handleError);
}
Exported class:
export class VehicleTypes {
// vehicleGroup: string;
vehicleType: string;
vehicleModel: string;
}
And here I'm calling that method in the separate file:
getVehicleGroups() {
return this.transformersService.getVehicleGroups(this.vehicleGroup)
.subscribe((vehicleTypes => this.vehicleTypes = vehicleTypes));
}
The url of the fake-server "http://localhost:3000/vehicleTypes" and this is the code from db.json on that server (url):
[
{
"group": "Air",
"type": "Plane",
"model": "F-22"
},
{
"group": "Air",
"type": "Plane",
"model": "Sukhoi"
},
{
"group": "Air",
"type": "Plane",
"model": "MiG"
},
{
"group": "Air",
"type": "Helicopter",
"model": "Apache"
},
{
"group": "Air",
"type": "Helicopter",
"model": "Kamov"
}
{
"group": "Sea",
"type": "Boat",
"model": "Sailboat"
},
{
"group": "Sea",
"type": "Boat",
"model": "Jetboat"
},
{
"group": "Sea",
"type": "Submarine",
"model": "Standard"
},
{
"group": "Land",
"type": "Car",
"model": "Camaro"
},
{
"group": "Land",
"type": "Car",
"model": "AMG GT R"
},
{
"group": "Land",
"type": "Car",
"model": "Lamborghini"
},
{
"group": "Land",
"type": "Truck",
"model": "Unimog"
},
{
"group": "Land",
"type": "Truck",
"model": "Western Star 5700"
}
]
I need to mention, all my files are set well. I don't get any errors, I'm just not getting the right values..
I need to get all "type": "values" where group is Air
First you need do filter your json result to get Air group only.
You can apply observable filter
getVehicleGroups(groupName: string) {
return this.http.get(`${this.apiUrl}/vehicleTypes?group=${groupName}`)
.filter(data => data.group === "Air")
.map((res: Response) => res.json() as VehicleTypes[]).catch(this.handleError);
}
Second your VehicleTypes model variable names are different with json response so how will angular convert your json array into VehicleTypes array. you need change VehicleTypes class or your backend code send match variables name.
export interface VehicleTypes {
type: string;
model: string;
}
Adjust the brackets of your method.
From:
getVehicleGroups() {
return this.transformersService.getVehicleGroups(this.vehicleGroup)
.subscribe((vehicleTypes => this.vehicleTypes = vehicleTypes));
}
To:
getVehicleGroups() {
return this.transformersService.getVehicleGroups(this.vehicleGroup)
.subscribe((vehicleTypes) => this.vehicleTypes = vehicleTypes);
}
Map the data with your model:
Option 1: Change the model to match the json data.
export class VehicleTypes {
type: string;
model: string;
}
Option 2: Change the json properties at service level, right after converting to json.
getVehicleGroups(groupName: string) {
return this.http.get(`${this.apiUrl}/vehicleTypes?group=${groupName}`)
.map((res: Response) => res.json().map(res => new VehicleTypes(res.type, res.model)).catch(this.handleError);
}
and you would need to create a constructor for the VehicleTypes.
You do not need a class in this case, a type interface will suffice since you don't have (or seem to need) any methods in your vehicle, you're only using it for type assertion.
A class exists in the emitted JavaScript incurring unnecessary overhead, whereas an interface provides type safety without emitting classes to JavaScript: it's only used by the type-checker in tsc and then discarded.
export interface VehicleTypes {
// vehicleGroup: string;
vehicleType: string;
vehicleModel: string;
}
Declare the type that your service returns:
getVehicleGroups(groupName: string): Observable<VehicleTypes[]> {
return this.http.get(`${this.apiUrl}/vehicleTypes?group=${groupName}`)
.map(res.json)
.catch(this.handleError);
}
And consume it in your Component as:
// Assert the type vehicleTypes expects
vehicleTypes: <VehicleTypes>[];
getVehicleGroups() {
return this.transformersService.getVehicleGroups(this.vehicleGroup)
.subscribe(vehicleTypes => this.vehicleTypes = vehicleTypes);
}
Note that you don't need the (res: Response) assertion in the chain from http.get: that's what get is typed to return anyway so the type-checker already knows what to expect. Since you can remove the parameter, you can make the chain even shorter, as you did with .catch.

Access Array inside an object (json)

I have this json file and I want to access the array that is inside this object:
best-sellers": [
{
"title": "Chuteira Nike HyperVenomX Proximo II Society",
"price": 499.90,
"installments": {
"number": 10,
"value": 49.90
},
"high-top": true,
"category": "society",
"image": ""
},
{
"title": "Chuteira Nike HyperVenom Phantom III Cano Alto Campo",
"price": 899.90,
"installments": {
"number": 10,
"value": 89.90
},
"high-top": true,
"category": "campo",
"image": ""
}
}
]
This is the code on my component:
ngOnInit(): void {
this.service
.lista()
.subscribe(chuteiras =>{
this.chuteiras = chuteiras;
})
}
and my template looks like this:
<div *ngFor="let chuteira of chuteiras.best-sellers">
But angular is not reconigzing it the `best-sellers", here's the error that I'm getting:
Cannot read property 'best' of undefined
Just use bracket notation,
<div *ngFor="let chuteira of chuteiras["best-sellers"]">
well,that's one way of doing it but angular 6 came with a simple solution. when i was faced with this problem i myself resolved to this solution but it didn't work for me so after searching and making my own touches i ended up with this solution.
1.create the function to receive the JSON data in my case i used a web API
getTrending() {
return this.http.get(https://api.themoviedb.org/3/trending/all/day?api_key=${this.api_key});
}
2.call the function in my case i used a service, so after import it to my component i simply added this
showPopular(): void {
this.api.getTrending().subscribe((data: Array<object>) => {
this.list = data['results'];
console.log(this.list);
});
}
as you can see the data variable only accessed the information i required.

map json response into nested typescript object with rxjs

In my Angular2-App I´m receiving a JSON-Response via http-Request that kind of looks like that:
{
"documents": [
{
"title": "Example-Doc 1",
"versions": [
{
"fileSize": 15360
},
{
"fileSize": 2048
}
]
},
{
"title": "Example-Doc 2",
"versions": [
{
"fileSize": 15360
},
{
"fileSize": 2048
}
]
}
],
"meta": {
"total": [2]
}
}
Now i wonder how to map this structure into my TypeScript-Classes, i checked different approaches, but it never worked. I actually need the constructor of the Version class to be called.
export class Document {
title: string; // Titel des Dokuments
versions: Version[];
}
If you have complex classes that need to be serialized and deserialized, I suggest that you implement static methods to your classes like fromJson and toJson - however without knowing your classes the rest of the answer will be kind of a guess-work:
Assuming you have a fromJson in place, then you can map your data like the following:
const myDocuments: Document[] = myJson.documents.map(Document.fromJson);
And the fromJson-method could look like this:
class Document {
constructor(public title: string, public versions: Version[])
public static fromJson(json: any): Document {
return new Document(
json.title,
json.versions.map(Version.fromJson)
);
}
}
class Version {
constructor(public fileSize: number) {}
public static fromJson(json: any): Version {
return new Version(json.fileSize);
}
}