How to fetch and use data from a JSON file in Angular7 - json

I am trying to fetch data from a JSON file and display that data in the form
JSON FILE Link:
https://raw.githubusercontent.com/datameet/railways/master/trains.json
I am trying with the below code. But it returns following error in fetchdata.component.ts file:
Property 'json' does not exist on type 'Object'.
fetchdata.component.ts
import { Component, OnInit } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Component({
selector: 'app-fetchdata',
templateUrl: './fetchdata.component.html',
styleUrls: ['./fetchdata.component.css']
})
export class FetchdataComponent implements OnInit {
private _trainUrl = "https://raw.githubusercontent.com/datameet/railways/master/trains.json
";
items : any;
constructor(private http:HttpClient) {
this.http.get( this._trainUrl)
.subscribe(res => this.items = res.json());
console.log(this.items);
}
ngOnInit() {
}
}
fetchdata.component.html
<select>
<option *ngFor="let item of items" [value]="item.properties.from_station_name">{{item.properties.from_station_name}}</option>
</select>
Please help.

The response probably isn't what you think. I suggest you console.log() the response of your query to see what it actually looks like:
items : any;
constructor(private http:HttpClient) {
this.http.get( this._trainUrl)
.subscribe(res => {
this.items = res.features;
console.log("Response", res);
console.log(res.features)
});
}
You'll see that you actually get something like this in your console:
{type: "FeatureCollection", features: Array(5208)}
features: (5208) [{…}, …]
type: "FeatureCollection"
__proto__: Object
So you can assign your items to the features key as that's what you really need:
constructor(private http:HttpClient) {
this.http.get( this._trainUrl)
.subscribe(res => {
this.items = res["features"];
});
}
Then your select options should show up.
Just letting you know, this isn't the perfect way to do it but it works fine for a small example like this.
I suggest you look into creating a service for any request in the future (doing it in the constructor isn't the best way) and have a look at the RxJS library

There is a difference between Angular's Http and HttpClient module and they are also exported differently.
Http -> is the core module which requires the user to call res.json(). This was common prior to Angular version 4.0.
HttpClient -> is new module since version 4.0. It defaults the communication to json and hence you don't need to call res.json() explicitly.
In short, changing from res.json() to just res will fix the issue
for you.
i.e this.items = res; should be fine.
Also, as a good practice use the ngOnInit lifecycle method instead of the constructor to make any Http calls.

Why do you do this.items = res.json()? Why not just this.items = res? res should already hold the JSON object returned from the GET request. If it is indeed a string try this.items = JSON.parse(res).

Can you try :
private _trainUrl = "https://raw.githubusercontent.com/datameet/railways/master/trains.json";
items : any;
constructor(private http:HttpClient) {}
ngOnInit() {
this.http.get( this._trainUrl).subscribe(res => {
this.items = res;
console.log(this.items);
});
}

You don't need to call .json() function in case you doing plain this.http.get. Angular does that for you. Simply do this.items = res. That will do the trick.
UPD: your JSON object is not an array itself. You as well need to update your template in the following way:
<select>
<option *ngFor="let item of items.features" [value]="item.properties.from_station_name">{{item.properties.from_station_name}}</option>
</select>

Related

Angular 2+ - Parse JSON as number

I want to place a marker on Google Maps using Angular Google Maps my coordinates are from JSON file which when parses in Angular it's become a string.
Angular Google Maps does not support string for coordinates number. So It needs to be number.
marker.json
[
{
"id":"1",
"lat":"13.751814",
"lon":"100.501060"
},
{
"id":"2",
"lat":"13.738445",
"lon":"100.516805"
},
{
"id":"3",
"lat":"13.730209",
"lon":"100.779991"
}
]
maps.componenet.ts
import { Component, OnInit } from '#angular/core';
import {Http, Response} from '#angular/http';
#Component({
selector: 'app-maps',
templateUrl: './maps.component.html',
styleUrls: ['./maps.component.css']
})
export class MapsComponent implements OnInit {
private data;
constructor(private http:Http){
}
ngOnInit(){
this.getData();
}
getData(){
this.http.get('/localhost/marker.json')
.subscribe(res => this.data = res.json());
}
}
maps.component.html
<agm-map [latitude]="13.359665" [longitude]="101.035913" [zoom]="6">
<ng-container *ngFor="let list of data">
<agm-marker [latitude]="list.lat" [longitude]="list.lon"></agm-marker>
</ng-container>
</agm-map>
I have tried using parseFloat like this (Yes it's not working)
<agm-marker [latitude]="parseFloat(list.lat)" [longitude]="parseFloat(list.lon)"></agm-marker>
I'm thinking of using parseInt inside maps.component.ts file but I'm not sure where to put this.
May someone helps me or guide me how to solve this.And please let me know that am I providing sufficient information or not.
Thanks!
A cleaner and more performant way is to process data once and provide it in view in a way that doesn't need any other preparations:
getData(){
this.http.get('/localhost/marker.json')
.map(res => res.json())
.map((data: any[]) => data.map(
({ lon, lat, ...props }) => ({ lon: +lon, lat: +lat, ...props })
))
.subscribe(data => {
this.data = data;
});
}
If marker.json doesn't have other props except listed ones, ...props can be replaced with known id property.
Global functions like parseFloat are not allowed in Angular templates (see the Angular documentation).
You can convert strings to numbers with the + operator:
<agm-marker [latitude]="+list.lat" [longitude]="+list.lon"></agm-marker>
or define a method in your component:
convertCoordinate(str: string): number {
return parseFloat(str);
}
and call it in the template:
<agm-marker [latitude]="convertCoordinate(list.lat)" [longitude]="convertCoordinate(list.lon)"></agm-marker>
+lat won't work in Angular template, it's invalid syntax and will throw an error, as well as parseFloat or parseInt, in here you can parse the JSON in the request like #estus pointed out OR you can multiply the string by 1:
<agm-marker [latitude]="list.lat * 1" [longitude]="list.lon * 1"></agm-marker>
This will transform the string into a number but beware that if it's NOT a number there will be error and you won't be able to catch it.

Loading data from a json file with an Interface

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

Getting [Object Object] in angular2 application

I have developed angular2 application using ngrx/effects for making http calls. I have used GIT as reference application. Once the response come from http, i am not able to display it on screen. Its showing [object Object]. Here is my code.
HTML page linked to component.html
<div class="container">
<div class="left-container cf">
<mat-tab-group>
<mat-tab label="Configuration">{{jsons}}</mat-tab>
<mat-tab label="Captured Output">
</mat-tab>
</mat-tab-group>
</div>
</div>
Component.ts
export class ExperimentDetailsComponent implements OnInit {
jsons: Observable<any>;
isLoading: Observable<any>;
constructor(
private store: Store<fromStore.State>
) {
this.isLoading = store.select(fromStore.getIsLoading);
this.jsons = store.select(fromStore.getJson);
console.log(this.jsons)
}
ngOnInit() {
this.store.dispatch(new jsonAction.GetJson());
// this.jsons = this.store.select(fromStore.getJson);
}
}
Effects.ts
export class GetJsonEffects {
#Effect() json$ = this.actions$.ofType(Act.GET_JSON)
.map(toPayload)
.withLatestFrom(this.store$)
.mergeMap(([ payload, store ]) => {
return this.http$
.get(`http://localhost:4000/data/`)
.map(data => {
return new Act.GetJsonSuccess({ data: data })
})
.catch((error) => {
return Observable.of(
new Act.GetJsonFailed({ error: error })
);
})
});
constructor(
private actions$: Actions,
private http$: HttpClient,
private store$: Store<fromStore.State>
) {}
}
As you see, the result of store.select() is an observable. You cannot data bind to it directly.
You can either:
Use the async pipe to make the UI subscribe to the observable for you and extract the data, like:
<mat-tab label="Configuration">{{jsons | async}}</mat-tab>
Or subscribe yourself to the observable.
export class ExperimentDetailsComponent implements OnInit {
jsonSubscription = store.select(fromStore.getJson)
.subscribe(jsons => this.jsons = jsons);
ngOnDestroy() {
this.jsonSubscription.unsubscribe();
}
jsons: any;
// ...
}
That's one thing:
If you are using Http service (from #angular/http module):
The other thing is that you are returning the Response object not the JSON extracted from it. The map() in your effect needs to call data.json(). Like:
return this.http$
.get(`http://localhost:4000/data/`)
.map(data => {
return new Act.GetJsonSuccess({ data: data.json() })
})
Or, as I like, add another map() to make things clear:
return this.http$
.get(`http://localhost:4000/data/`)
// You could also create an interface and do:
// `response.json() as MyInterfaceName`
// to get intellisense, error checking, etc
.map(response => response.json())
.map(data => {
return new Act.GetJsonSuccess({ data: data })
})
If you are using HttpClient service (from #angular/common/http module):
(Available in Angular v4.3+)
In this case you don't need to call .json() yourself, it does it for you, so you don't need that first .map() I suggested.
You can also tell TypeScript about the type you expect the JSON to match by calling the get() like this:
return this.http$
.get<MyInterfaceName>(`http://localhost:4000/data/`)
.map(data => {
return new Act.GetJsonSuccess({ data: data.json() })
})
The get<MyInterfaceName>() bit will make Angular tell TypeScript that the JSON object matches the MyInterfaceName, so you'll get intellisense and error checking based on this (at compile time only, none of this affects runtime in anyway).
HttpClient Documentation

http observable<any> - Angular 4

I need to display the data on html that I get from web service. I am able to see the data in a format that I want, but I can't display properly on html. I think -any- in http.get is the problem. I can read data in console without -any- but it works fine with . When it works with it, it still does not print in html properly. Can anyone provide advice on this?
html
<div>{{this.res}}</div>
app.component.ts
import { Component, OnInit } from '#angular/core';
//import { IMovie } from './movie';
import { AppService } from './app.service';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
res: any[] ;
errorMessage: string;
constructor(private _appService: AppService) { }
ngOnInit(): void { this.getData(); }
getData(): void {
this._appService.getData()
.subscribe(
(res: any []) => this.res = res,
(error: any) => this.errorMessage = <any>error);
}
}
app.service.ts :
Injectable()
export class AppService {
private urlNorth = '';
constructor(private http: HttpClient) { }
getData(): Observable<any> {
const headers = new HttpHeaders();
headers.set('Content-Type', 'text/sml');
headers.set('Accept', 'text/xml');
headers.set('Content-Type', 'text/xml');
return this.http.get<any>(this.urlNorth,{responseType:'text', headers: headers})
.do(data => {
// console.log(data)
var dataParsed = data.replace('<string xmlns="service">', '').replace('</string>', '').replace(/</g, '<').replace(/>/g, '>');
// console.log(dataParsed);
parseString(dataParsed, (err, res) => {
if (err) {
return console.dir('invalid XML');
}
else {
console.log(res);
console.log(res.NewDataSet.Table[0].DataPointName[0]);
}
})
})
.catch(this.handleError);
}
**data in console w/o any **
{{this.res}} in html
I'm pretty sure you don't have to put any at this line in app.service.ts
return this.http.get<any>(this.urlNorth,{responseType:'text', headers: headers})
because get method expects 0 type arguments.
Type any is not the problem. It's just TypeScript annotation to organise your code. The problem is you are refering to the res in inline template as this.res, but you should just res. However it won't work as you think. Looking at your data structure You will have to iterate throught this data due to Table is an array. Additionaly I Highly suggest to always represnt your data as class
export class Apps {
public Table: Array<any>; //here should be another type instead of "any"
/* rest of data if any */
}
Back to your question you should have in your html file <div>{{res}}</div> but that's just print your object as string if I good remember. So to properly access your data you should iterate through table using *ngFor
<div *ngFor="let el of res.NewDataSet.Table">
<span>{{el.BackColor}}</span>
<!-- all other data -->
</div>
It looks as though the data is coming back. I'll answer your initial question first (since you added a few issues in comments):
My guess is when you get data back, it's not showing because it's HTML, and angular doesn't like injecting html.
Add this to your TS:
import { DomSanitizer, SafeHtml } from '#angular/platform-browser';
res[]: safeHTML;
And change your html to this:
<div [innerHTML]="res"></div>
As mentioned in a previous answer, this is a solution for a single return of res, not an array of different htmls. If it's an array, you'll have to handle it accordingly. for instance:
<ng-container *ngFor="let r of res">
<div [innerHTML]="r">
</ng-container>

http with Observable in Angular 2 cant use data

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});
}