How to get the length of JSON array? - json

I have a json url with a response of the following kind
[
{
"gender": "Female",
"age": 7,
"class": 2,
"subject": [
"",
"",
""
]
},
{
"gender": "Female",
"age": 8,
"class": 3,
"subject": [
"ab",
"cd",
"ef"
]
},
...
]
I want to find the length of this array
so in the angular app I am using
export class CustomComponent implements OnInit {
length: number;
constructor() {
this.length = Object.keys('www.url...').length;
console.log(length);
}
in .html I have
<p>{{ length }}</p>
I am getting 61 whereas the actual length is 44, but the console on the other hand is showing 0.
Where am I going wrong?
Any kind of help would be greatly appreciated. Thanks.

Easiest method.
import { Http } from '#angular/http';
export class CustomComponent implements OnInit {
length: number;
constructor(private http: Http){
this.http.request('www.url...',{method:'GET'}).map(response => response.json()).subscribe(result=>{
this.length=result.length;
});
}
}

Issue here is you are trying to execute Object.keys on url and not on
data , your url will be considered as string.
Solution is to use HttpClient :
// First get data from external url and then perform all operation
this.http.get('www.url...').subscribe(data => {
this.length = data.length;
console.log(length);
});

Related

initialize json file data as a TypeScript Map

as a young typescript padawan
i am trying to parse mock data from a json witch involves initializing a Map<string, MenuPageData>
and i get an error (attached below)
i would like to be able to supply the correct data format in the json or map it properly from the existing data.
here is the error:
here is the code:
export interface MenuItem {
data: {
id: string;
name: string;
currency?: string;
imageUrl?: string;
description?: string;
price?: number;
};
}
export interface MenuPageData {
pageName: string;
menuItems: MenuItem[];
}
export interface MenuPageDataCollection {
menuPages: Map<string, MenuPageData>;
}
the error is comming from "(pagesDataMock.menuPages)"
const dataCollection: MenuPageDataCollection = { menuPages: new Map<string, MenuPageData>(pagesDataMock.menuPages) };
export const menuPagesCollection: () => MenuPageDataCollection = (): MenuPageDataCollection => {
return dataCollection;
};
and here is the json source:
{
"menuPages": [
"mealPage1",
{
"pageName": "menuPage1",
"menuItems": [
{
"data": {
"id": "null0",
"name": "meal",
"currency": "EUR",
"imageUrl": "../images/greenHand.jpg",
"description": "tasty",
"price": 12
}
},
{
"data": {
"id": "null0",
"name": "meal",
"currency": "EUR",
"imageUrl": "../images/greenHand.jpg",
"description": "tasty",
"price": 12
}
}
]
},
"mealPage2",
{
"pageName": "menuPage1",
"menuItems": [
{
"data": {
"id": "null0",
"name": "meal",
"currency": "EUR",
"imageUrl": "../images/greenHand.jpg",
"description": "tasty",
"price": 12
}
},
{
"data": {
"id": "null0",
"name": "meal",
"currency": "EUR",
"imageUrl": "../images/greenHand.jpg",
"description": "tasty",
"price": 12
}
}
]
}
]
}
i would appreciate and feedback, i tried several different ways of doing it but now 3-4 days passed by and i am running on empty of understanding
Map is a rather strong constraint that is not representable in JSON as is. First you'll have to validate the input is satisfying the requirements for your Map<string, MenuPageData>. Then to make it understandable by TS wrap in into type guard or type assert function. And finally convert it into the actual Map object yourself.
Simplified example:
interface IFace {
prop: string
}
interface Res {
title: string
ifaceMap: Map<string, IFace>
}
interface ResJSON {
title: string
ifaceMap: { [K in string]: IFace }
}
function assertResponse(json: unknown): asserts json is ResJSON {
...
}
function convert2Res(res: ResJSON): Res {
const { title, ifaceMap } = res
const map = new Map()
for(let key in ifaceMap) map.set(key, ifaceMap[key])
return { title, ifaceMap: map }
}
assertResponse(goodResponse)
const mapRes = convert2Res(goodResponse)
TS playground
In case you have full control of the source json and absolutely sure it may never be misshaped you may completely skip the assert part and feed the response (forcibly asserted to ResJSON type) right into convert2Res.

Angular dynamic forms created with JSON data

Dear reader. I am trying to make dynamic forms with Json data that has been red. The dynamic form is based on the example of Angular as seen here: https://angular.io/guide/dynamic-form
The edits I made are that I read data from an external Json file and try to load those instead of the hardcoded one in the file 'question.service.ts' as seen in the link.
This is how my Json file looks like:
{
"formInfo": {
"name": "test"
},
"fields": [
{
"controlType": "textbox",
"key": "firstName",
"label": "Voornaam",
"required": true,
"value": "Mark",
"order": 1
},
{
"controlType": "textbox",
"key": "surName",
"label": "Achternaam",
"required": true,
"order": 2
},
{
"controlType": "textbox",
"key": "emailAddress",
"label": "Email",
"required": false,
"order": 3
},
{
"controlType": "dropdown",
"key": "brave",
"label": "Beoordeling",
"required": "",
"order": 4,
"options": {
"solid": "Solid",
"great": "Great",
"good": "Good",
"unproven": "Unproven"
}
}
]
}
And my function to retrieve the data and return as observable (in question.service.ts) looks like:
getQuestions2() : Observable<QuestionBase<any>[]> {
let questions: QuestionBase<any>[] = [];
const exampleObservable = new Observable<QuestionBase<any>[]>((observer) =>
{
let url = "../assets/exampleData.json"
this.http.get(url).subscribe((data) => {
for (let x of data['fields']){
if (x.controlType == "textbox"){
let textboxItem = new TextboxQuestion({
key: x.key,
label: x.label,
value: x.value,
order: x.order
})
questions.push(textboxItem);
}
else if (x.controlType == "dropdown"){
let dropDownItem = new DropdownQuestion({
key: x.key,
label: x.label,
value: x.value,
options: x.options,
order: x.order
})
questions.push(dropDownItem);
}
}
})
observer.next(questions.sort((a, b) => a.order - b.order));
})
return exampleObservable;
}
and the code that connects the service with the frontend looks like this:
export class AppComponent implements OnInit {
questions: any[];
constructor(private service: QuestionService) {
this.getaSyncData();
//this.questions = this.service.getQuestions();
//console.log(this.questions);
}
getaSyncData(){
this.service.getQuestions2()
.subscribe((data) => this.questions = data);
console.log(this.questions);
}
I solved this finally for those who will have similar issues in the future
I was not able to load forms into the html even though I was correctly reading the data out of the JSON file and printing it in the console. I added a *ngIf in the div where you load in your data. In the example of Angular.io its in the template on App.component.html. Yes, it was this simple.

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.

How to read in Angular 2 the JSON #text

Sorry, but I'm stagnant to read a very specific JSON value in Angular 2&4, where I can obtain and showing the name of the list BUT! but I can't find a way to show in the page, what I'm talking about for example in the JSON document I have this way: (obtained from http://www.last.fm/api/show/artist.getTopAlbums ) and I'm using the Postman to check all the contents, I can obtain the name of the Album and other things, BUT to obtain the #text to use the image it's weird
"topalbums": {
"album": [
{
"name": "The Very Best of Cher",
"playcount": 1663904,
"mbid": "a7e2dad7-e733-4bee-9db1-b31e3183eaf5",
"url": "https://www.last.fm/music/Cher/The+Very+Best+of+Cher",
"artist": {
"name": "Cher",
"mbid": "bfcc6d75-a6a5-4bc6-8282-47aec8531818",
"url": "https://www.last.fm/music/Cher"
},
**// +++++++++++++++++++THIS PART +++++++++++++++++++++!!!!!**
"image": [
{
// I'M TRYING TO USE THIS INFORMATION
"#text": "https://lastfm-img2.akamaized.net/i/u/34s/287bc1657795451399d8fadf64555e91.png",
"size": "small"
},
{
"#text": "https://lastfm-img2.akamaized.net/i/u/64s/287bc1657795451399d8fadf64555e91.png",
"size": "medium"
},
{
"#text": "https://lastfm-img2.akamaized.net/i/u/174s/287bc1657795451399d8fadf64555e91.png",
"size": "large"
},
{
"#text": "https://lastfm-img2.akamaized.net/i/u/300x300/287bc1657795451399d8fadf64555e91.png",
"size": "extralarge"
}
]
},
What I want it's to obtain the "url" of the image, I try to obtain in this way:
artist.component.html
<div *ngFor="let list of albumes">
{{list.name}}
<hr>
<!-- Can't obtain the URL... -->
<img src="{{list.image[1].text}}" alt="">
</div>
artist.component.ts
import { Component, OnInit } from '#angular/core';
import {ActivatedRoute} from '#angular/router';
import {MusicService} from "../../services/music.service";
#Component({
selector: 'app-artist',
templateUrl: './artist.component.html',
providers: [MusicService]
})
export class ArtistComponent implements OnInit {
constructor(private service: MusicService, private route: ActivatedRoute) { }
albumes: any[];
songs: any[];
ngOnInit() {
this.route.params.map(params => params['mbid']).subscribe(
mbid => {
// Top Album
this.service.GetTopAlbumArtist(mbid).subscribe(topalbum => {
this.albumes = topalbum.topalbums.album;
console.log(topalbum.topalbums.image);
});
// Top Tracks
// this.service.GetTopTracksArtist(mbid).subscribe(toptracks => {
// this.songs = toptracks;
// });
}
)
}
}
First, if you want to get the first position of image array, you should access the position 0 (not 1)... also you don't have text but #text:
To solve your problem you can do this way:
src="{{list.image[0]['#text']}}"
or even better:
[src]="list.image[0]['#text']"

Using *ngFor with a JSON object in Angular 2

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