I'm having a tough time understanding how to access different aspects of an JSON object in Angular2. Particularly, I have a web API that I built that returns the following JSON object regarding the hard drive details on my server:
The image is a screenshot of my console in Chrome after using an httpService and Observable to push it to the console but understanding how to get to a specific piece of info is getting lost on me.
If someone could point me in the right direction, it would be greatly appreciated.
After having subscribed to the http Observable you have already got the actual object.
Assuming your http get request looks like this:
this.httpService.get(this.apiUrl);
you can use the power of rxjs Observables, for example map over the object like this:
this.httpService.get(this.apiUrl)
.map(res => res.json())
.map(body => body.Data)
.map(data => data[0].AvailableSpace)
which after subscribing to would return the AvailableSpace.
.subscribe(availablespace => console.log(availablespace);
Watch out for accessing arrays like this, this is just to give you an example on how to access and manipulate objects in observables.
Check this site out for more information on different observable
operators, other than map.
https://www.learnrxjs.io/
Let me try my luck. Hope it will help people understand better. Particularly, I will talk about how to perform get request in Angular 2. It is always better to have a get and post request in a separate file called service.ts as mentioned in the official documentation.
We will have three files, namely example.component.ts, example.service.ts and Model file examplemodel.ts
example.component.ts
import {OnInit, Component} from "#angular/core";
import {ExampleService} from "./example.service"; // import service
import {ResponseFromGet, ErrorMessage} from "./examplemodel"; // import your model
#Component({
providers: [ExampleService], // add your service here in order to use in component file
templateUrl: './example.template.html'
})
export class ExampleComponent implements OnInit{
//Specify Url for Get request
Private _getRequestUrl = "http://entergetrequesturlhere";
// Make variable for stroing get method reponse which can be used in ur template file
responseFromGetMethod: ResponseFromGet; // this can be ur model file which represnts ur JSON model
// For storing Error
errorMessage: ErrorMessage;
//use Constructor to inject your service in component file
constructor(private _exampleService: ExampleService){}
// Since we implemented OnInit we need to override its method ngOnInit
// this method is called when page is loaded
ngOnInit(): any{
this.callGetMethod(this._getRequestUrl);
}
// callGetMethod outside OnInit but inside class ExampleComponent
callGetMethod(getUrl: string){
this._exampleService.getMethodName(getUrl)
.subscribe(
responseFromGetMethod => {
this.responseFromGetMethod = responseFromGetMethod; // Store response from getmethod in your local variable
},
error => this.errorMessage = <any>error // Store error message receiver from server
);
}
}
example.service.ts
import {Http, Response} from "#angular/http";
import {Injectable} from "#angular/core";
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/catch';
import {ResponseFromGet} from "./examplemodel";
#Injectable()
export class ExampleService{
constructor(private _http: Http) { }
// GET request To access data and specify observable type from model
getMethodName(getUrl): Observable<ResponseFromGet>{
return this._http.get(getUrl)
.map(this.extractData) // to check for the status code
.catch(this.handleError); // to check error
}
// Extracts from response
private extractData(res: Response) {
if (res.status < 200 || res.status >= 300) {
throw new Error('Bad response status: ' + res.status);
}
let response = res.json();
return response || {};
}
// To handle Error
private handleError(error: Response) {
console.error(error);
return Observable.throw(error.json() || 'Server error');
}
}
examplemodel.ts
export interface ResponseFromGet{
id: number;
name: string;
}
export interface ErrorMessage{
message: string;
}
And finally the HTML file
example.template.html
<div>
<h2>{{responseFromGetMethod?.name}}</h2> // this will print the name from the json file
<h3>{{errorMessage?.message}}</h3> // this will print the error if any
</div>
Lastly, this is the model of my JSON file
{
"id": 789;
"name": "Angular2";
}
Related
Trying to follow an online video, then this appears, I am new to angular, other solutions are not helping me out.
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
import 'rxjs/add/operator/map';
/*
Generated class for the WeatherProvider provider.
See https://angular.io/guide/dependency-injection for more info on providers
and Angular DI.
*/
#Injectable()
export class WeatherProvider {
apikey='7d2dc7a226a78c14';
url;
constructor(public http: HttpClient) {
console.log('Hello WeatherProvider Provider');
this.url='http://api.wunderground.com/api/'+this.apikey+'/conditions/q'
}
getWeather(city,state){
return this.http.get(this.url+'/'+state+'/'+city+'.json')
.map(res => res.json() );
}
}
If you're using the new HttpClient you don't need to parse JSON because it's decoded automatically for you:
https://angular.io/guide/http#type-checking-the-response
The HttpClient.get() method parsed the JSON server response into the anonymous Object type. It doesn't know what the shape of that object is.
Also https://angular.io/guide/http#requesting-non-json-data.
With angular 5 and httpClient, you don't need to use the map part anymore.
Read more here: https://angular.io/guide/http#type-checking-the-response
getWeather(city,state){
return this.http.get(this.url+'/'+state+'/'+city+'.json');
}
If you want to get data in specific format, You can tell HttpClient the type of the response to make consuming the output easier and more obvious.
export interface Config {
heroesUrl: string;
textfile: string;
}
And then :
getConfig() {
// now returns an Observable of Config
return this.http.get<Config>(this.configUrl);
}
I'm writing a little ionic app for learning purposes and I would like to load data from a json file and assign it to an Interface that describes the data. But I'm struggling with getting it the right way:
import { Component } from "#angular/core";
import { HttpClient} from "#angular/common/http";
export interface PhonebookEntry {
name: string,
telephone: string,
description: string
}
#Component({
selector: 'page-phonebook',
templateUrl: 'phonebook.html'
})
export class PhonebookPage {
entries: Array<PhonebookEntry>;
constructor(public http: HttpClient) {
this.load_entries('assets/json/phonebook.json');
};
load_entries(filePath: string) {
return this.http.get(filePath)
.subscribe(
data => this.entries = data
);
};
}
I think only the line data => this.entries = data is wrong (also the IDE is telling me that), but I don't know to do this right and can't find documentation describing the correct way. If there actually is some I would be glad to know where I can find ressources about this.
subscribe return the response as an object, not as an array. So entries type should be changed.
entries: PhonebookEntry;
In the subscribe, need to assign a type for response data.
load_entries(filePath: string) {
return this.http.get(filePath)
.subscribe(
(data: PhonebookEntry) => this.entries = data // or use any type
);
};
Demo
import { Injectable } from '#angular/core';
import { Http,Response } from '#angular/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
#Injectable()
export class CommentService{
private _url :string ="https://jsonplaceholder.typicode.com/posts"
constructor(private _http:Http){}
// method to fetch CommentS from a api service
getComments(){
return this._http.get(this._url)
.map((response:Response)=> response.json())
.catch(this._errorHandler);
}
_errorHandler(error:Response){
console.error(error);
return Observable.throw(error ||"Server Error");
}
}
the above code works great with this url https://jsonplaceholder.typicode.com/posts
but does not work with this url http://ergast.com/api/f1/2016/driverStandings.json
any ideas ...TIA
Aravind is on the right track here, but MRData case sensitive, and mapping is a bit off. The correct way to map the response here would be:
return this._http.get(this._url)
.map((response:Response)=> response.json().MRData)
.catch(this._errorHandler);
}
Your component:
getData() {
this.service.getData()
.subscribe(data => {
this.data = data;
});
}
Then you can access data, e.g like:
<div>
<b>Series:</b> {{data?.series}}<br>
<a><b>Url:</b> {{data?.url}}</a>
</div>
Then you seem to have a lot of nested objects in your response, so this might help you: Access / process (nested) objects, arrays or JSON
Here's a demo with mock JSON, but I did try that url you provided and the data was received fine. So replicating the plunker should work fine in your app :)
DEMO
To make the second one work you need to get the data using
return this._http.get(this._url)
.map((response:Response)=> response.MRDATA.json())
.catch(this._errorHandler);
}
Because in this working one the data is in array of objects type Meaning
The one which does not work is completely single object
this is a link to maps.googleapis.com. You get JSON information about the latitude and longitude in the url.
I need to read this JSON using Typescript and Angular2.
I tried a lot of different google suggestions and (among others) the following code (suggested by angular on this link):
private extractData(res: Response) {
let body = res.json();
return body.data || {};
}
// this is fired when I click on my map, this.lat & this.lng are correctly filled
getLongLatClick($event: any) {
this.lat = $event.coords.lat;
this.lng = $event.coords.lng;
this.url = 'https://maps.googleapis.com/maps/api/geocode/json?latlng='+this.lat+','+this.lng+'';
console.log(this.http.get(this.url).map(this.extractData));
But when I debug in chrome, the "extractData" methode doesn't run.. It seems that the googleapis link isn't JSON for some reason
What do I have to do to read the JSON?
You should create a service that makes the http.get to get the data, similiar to :
import { Injectable } from '#angular/core';
import {Headers, Response, Http, RequestOptions} from "#angular/http";
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/toPromise';
#Injectable()
export class DataService{
private gmapsUrl: string = 'http://maps.googleapis.com/maps/api/geocode/json?latlng=52.48278022207823,6.15234375';
constructor(private http: Http) {};
getAll() {
return this.http.get(this.gmapsUrl).map((response: Response) => response.json());
}
}
Cool, now you have a service that gets the data, which is also injectable. You can inject this service into any consumers you wish and consume the data. That is similar to :
import {Component, OnInit, ElementRef} from '#angular/core';
import {DataService} from "path";
#Component ({
moduleId: module.id,
selector: 'custom',
templateUrl: //your metadata,
styleUrls: //your metadata
})
export class ConsumerComponent implements OnInit{
gmapsData: any = [];
constructor(private dataService:Data) {}
ngOnInit(): void {}
private loadAllUsers() {
this.dataService.getAll().subscribe(response=> {
console.log(response.results); //
this.gmapsData = response;
});
}
}
Hope this helps -> This should give you a solid starting point.
What I haven't actually checked is the mapping between the response of the dataService.getAll() inside the consumer to the actual component property gmapsData, but you should be able to infer how to store it from the console.log(response);
You are using the wrong code in your extractData. As you can see in the JSON response:
{
"results" : [
{
"address_components" : [
{
"long_name" : "20",
"short_name" : "20",
"types" : [ "street_number" ]
}
.......
it has results, not data.
So it should be:
private extractData(res: Response) {
let body = res.json();
return body.results || {};
}
So the following should work fine (using the static url in this example):
this.http.get('http://maps.googleapis.com/maps/api/geocode/json?latlng=52.48278022207823,6.15234375')
.map(this.extractData)
.subscribe(data => console.log(data))
Remember to always subscribe to get your response. And do consider making a service where you do the http-calls and map and then in your component call that service method and subscribe the results!
And it's good to check the network tab and see what the response looks like, and to see if you are getting a response at all.
Hope this helps! :)
i am new to angular 2 and to observables but i wanted to give it a shot. So i have installed the angular-cli and made a simple test project.
All i wanted it to do is read a json file and work with the data inside of a component (the first intention was to make a service but i wanted to start on a low basis).
So i have created a json file in the assets/json folder (testjson.json):
{
"teststring": "test works"
}
then i have imported the http from angular and the rxjs map stuff inside of my content.component.ts file:
import { Component, OnInit } from '#angular/core';
import { Http } from '#angular/http';
import 'rxjs/add/operator/map';
#Component({
selector: 'app-content',
templateUrl: './content.component.html',
styleUrls: ['./content.component.css']
})
export class ContentComponent implements OnInit {
title: string = "Default";
data;
constructor(private http:Http) {
http.get('assets/json/testjson.json').map(res => res.json()).subscribe(data => {this.data = data; this.title = data.teststring; console.log(this.data);});
}
ngOnInit() {
}
}
So far so good, the app prints out the following:
app works!
test works [object Object]
But i want to use this data in the whole component, not only in the constructor. but if i try to console.log "this.data" outside of the constructor (inside the ngOnInit function), it prints undefined in the console.
I know, that it must have something to do with asynch loading but unfortunately i have no clue how to tell the app to wait until this.data is filled.
I hope you can help me with that. Of course in the future i want a service which does that kind of stuff and more than one component should grab data from it.
Thanks in advance!
You should move the initialization code to the initialization method.
Your data becomes available once the callback completes. In your template you can use *ngIf to execute code inside a block once there is data. As long as the *ngIf does not eval to true the inner code will not run.
The only way you can run console.log(data) is from inside the callback or called from the callback because you have to wait until the data is loaded.
content.component.html
<div *ngIf="data">
<span>{{data.teststring}}</span>
</div>
content.component.ts
export class ContentComponent implements OnInit {
title: string = "Default";
data: any = null;
constructor(private http:Http) {
}
ngOnInit() {
this.http.get('assets/json/testjson.json')
.map(res => res.json())
.subscribe(data => {
this.data = data;
this.title = data.teststring;
console.log(this.data);
});
}
}
Edit
In response to the comment below If you abstract out the http call to a service you can see the exact same logic still applies. You are still using the concept of a promise of data and that you can subscribe to that promise once it has completed. The only difference here is the http call is abstracted to a different class.
content.component.ts
export class ContentComponent implements OnInit {
title: string = "Default";
data: any = null;
// inject service
constructor(private contentService:ContentService) {
}
ngOnInit() {
this.contentService.getData()
.subscribe(data => {
this.data = data;
this.title = data.teststring;
console.log(this.data);
});
}
Service
export class ContentService {
constructor(private http:Http) {
}
getData(): IObservable<{teststring:string}> { // where string can be some defined type
return http.get('assets/json/testjson.json')
.map(res => res.json() as {teststring:string});
}