I want to display a line chart from JSON data. I have used angular2-highcharts. The problem is that the chart is displayed without data. I think that the issue is from extracting data from JSON.
The JSON format looks like this:
[{"_id" : ObjectId("59049a7b223f1e21ee4ee23b"),"amount" : 1,"date" :
"Mon, 18 Dec 1995 18:28:35 GMT"},{"_id" :
ObjectId("59049a7b223f1e21ee4ee23b"),"amount" : 1,"date" : "Mon, 18
Dec 1995 19:28:35 GMT"}]
I need only the "amount" in the X value and the "date" in the Y value.
Here is my code
ChartistJs.service.js
import {Injectable} from '#angular/core';
import { Headers, Http, RequestOptions, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import { Data } from "./Data";
import 'rxjs/add/operator/toPromise';
private Url ='http://localhost:3000/transfer/chart';
constructor (private http: Http) {}
getData(){
return this.http.get(this.Url)
.toPromise()
.then(response => response.json())
.catch(this.handleError);
}
ChartistJs.component.ts
import {Component} from '#angular/core';
import {ChartistJsService} from './chartistJs.service';
import 'style-loader!./chartistJs.scss';
import { Observable } from "rxjs/Observable";
import { ChartModule } from 'angular2-highcharts';
import 'rxjs/Rx';
import {Observer} from 'rxjs/Observer';
import {Http, Jsonp} from '#angular/http';
#Component({
selector: 'chartist-js',
template: `
<chart [options]="options"></chart>
`,
providers : [ChartistJsService]
})
export class ChartistJs {
options: Object;
constructor(private _chartistJsService:ChartistJsService) {
var chartData = this._chartistJsService.getData();
this.options = {
title : { text : 'simple chart' },
xAxis: {
type: 'category'
},
series: [{
data: chartData
}]
};
}
}
Can you help me how to deal with JSON data in Angular 2?
As Pankaj points out you are trying to pass a promise as the data, not the actual data that the promise eventually resolves to. More broadly, though, you aren't really using the tooling that Angular provides for dealing with HTTP.
In general, I would recommend that you:
Get used to dealing with observables, which is what Angular uses natively, rather than converting everything back to promises (although I think they still show this in the docs); and
Lean into the asynchronous nature of the observables, using the AsyncPipe to resolve them into your templates and the objects that RxJS provides to manipulate the data flow.
More specifically, here's one way you could implement what you're currently trying to.
Service:
#Injectable()
class DataService {
// acts as a pipe for the data that you can push new items into
private dataSubject = ReplaySubject(1);
// takes the subject and exposes the result, read-only
chartData$ = this.dataSubject.asObservable();
constructor (private http: Http) {}
getData() {
// GETs the data and pushes it into the subject
this.http.get('http://localhost:3000/transfer/chart')
.map(response => response.json())
.subscribe(data => this.dataSubject.next(data));
}
}
Component:
#Component({
... ,
// resolves the chart options asynchronously in the template
template: `
<chart [options]="chartOptions$ | async"></chart>
`
})
export class MyChartComponent implements OnInit {
chartOptions$: Observable<any>;
constructor(dataService: DataService) {
// creates a new observable of the chart options
this.chartOptions$ = this.dataService.chartData$
.map(data => this.createChartOptions(data));
}
ngOnInit() {
// triggers a fetch of the data to feed the observable
this.dataService.getData();
}
private createChartOptions(data) {
return {
title: { text: 'simple chart' },
xAxis: { type: 'category' },
series: [{ data: data }],
};
}
}
You will probably need to do more to the JSON than just pass it as the series.data, but this hopefully gives you an idea of how to leverage the stream of events an observable can provide. I've written more about this on my blog, including a follow-up article on testing.
Also note that your components shouldn't be importing anything from '#angular/http' - leave that to the services, use them as a layer of abstraction from the source of the data - and you can load providers at the module, rather than component, level.
Actually chartData variable does hold Promise returned by getData method. Where you should keep .then over the getData method calla and assign options with chartData like shown below.
It would be more better if you can do the same in ngOnInit lifecycle event.
Code
export class ChartistJs {
options: Object;
constructor(private _chartistJsService: ChartistJsService) {}
ngOnInit() {
this._chartistJsService.getData().then(
(data) => {
this.options = {
title: {
text: 'simple chart'
},
xAxis: {
type: 'category'
},
series: [{
data: data
}]
};
}
);
}
}
Related
I have to make an Angular application in which i get data from the back-end and display it on the front-end, but with some added hard-coded data.
My communication is between 2 files:
client.service.ts
import { Injectable } from '#angular/core';
import {HttpClient, HttpHeaders} from "#angular/common/http";
import {environment} from "../environments/environment";
import {catchError, map, Observable, of} from "rxjs";
const clientUrl = environment.apiUrl+'client';
#Injectable({providedIn: 'root'})
export class ClientService {
public optional: any;
constructor(private http: HttpClient) {}
getText(): Observable<any> {
console.log("it works!");
return this.http.get(clientUrl+"/getText").pipe(map(res => {
console.log(res);
this.optional = res.toString();
}));
}
}
and the second one:
client.component.ts
import { Component, OnInit } from '#angular/core';
import {ClientService} from "../client.service";
#Component({
selector: 'app-client',
templateUrl: './client.component.html',
styleUrls: ['./client.component.css']
})
export class ClientComponent implements OnInit {
public textResponse: any;
constructor(public service: ClientService) {}
ngOnInit(): void {}
getText() {
let text: any;
this.textResponse = this.service.getText().subscribe();
console.log(this.textResponse);
text = this.textResponse + "This text is added from code.";
console.log(text);
}
}
When i call "this.http.get(clientUrl+"/getText")" I get a SafeSubscriber object, from which i managed to get the data displayed in the console using the method ".subscribe(...)" with a "console.log()" inside of it. However, i did not find any method to extract the data out of this subscribe.
As the code above shows, i have tried to use pipe and map, but the local variable is returned as [Object object], and when i print it in the console i get either undefined, either nothing.
This is what my code currently displays:
it works! [client.service.ts:33]
SafeSubscriber {initialTeardown: undefined, closed: false, _parentage: null, _finalizers: Array(1), isStopped: false, …} [client.component.ts]
[object Object]This text is added from code. [client.component.ts]
{text: 'This text is read from a file.'} [client.service.ts]
I have also tried all the suggestions found in questions below:
angular 2 how to return data from subscribe
Angular observable retrieve data using subscribe
Does anyone know a method in which i could get the data out of the Subscribe?
You are missing the return keyword when mapping the response, looking at the console.log, you need the text property
getText(): Observable<any> {
console.log("it works!");
return this.http.get(clientUrl+"/getText").pipe(map(res => {
console.log(res);
this.optional = res.toString();
return res.text;
}));
}
I'm trying to build an app with ionic that reads data from a local `.json' file and uses this data to fill a page. But I'm already struggling with importing the file into the page. What I currently have is:
import { Component } from "#angular/core";
interface Entry {
name: string,
telephone: string
}
interface EntryList {
entryList: Array<Entry>;
}
#Component({
selector: 'page-list',
templateUrl: 'list.html'
})
export class ListPage {
entryList: EntryList;
constructor() {
this.load_entries();
};
load_entries () {
this.entryList = JSON.parse(
// ?
)
};
}
The .json file contains entries like:
[
{"name": "Person A","telephone": "1234"},
{"name": "Person B","telephone": "12345"}
]
I don't know how to proceed from here on. What's the right way to get my data into the app?
Please try this:
constructor(public http: HttpClient) {
this.load_entries();
};
load_entries(filePath: string) { //filePath: 'assets/test.json'
this.http
.get(filePath)
.subscribe((data) => {
console.log(data);
});
}
Of course, you have to import HttpClient first.
import { HttpClient } from '#angular/common/http';
I am developing angular REST application using ngrx/effects, I am using example application GIT. I am trying to replace hardcoded json data in effects, from http REST end. I am getting errors "Effect "GetTodoEffects.todo$" dispatched an invalid action" . Could you please help me in solving it. Every thing is same as git code, except effects code which is i am pasting below.
Effects code:
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/withLatestFrom'
import { of } from 'rxjs/observable/of';
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import { Action, Store } from '#ngrx/store';
import { Actions, Effect, toPayload } from '#ngrx/effects';
import * as Act from '../actions/app.actions';
import * as fromStore from '../reducers';
import { HttpClient } from '#angular/common/http';
#Injectable()
export class GetTodoEffects {
#Effect() todo$ = this.actions$.ofType(Act.GET_TODO)
.map(toPayload)
.withLatestFrom(this.store$)
.mergeMap(([ payload, store ]) => {
return this.http$
.get(`http://localhost:4000/data/`)
.map(data => {
return [
new Act.GetTodoSuccess({ data: data })
]
})
.catch((error) => {
return [
new Act.GetTodoFailed({ error: error })
]
})
});
constructor(
private actions$: Actions,
private http$: HttpClient,
private store$: Store<fromStore.State>
) {}
}
I am using json-server as REST end point. json-server --port 4000 --watch expt-results-sample.json
expt-results-sample.json
[
{
text: "Todo 1"
},
{
text: "Todo 2"
},
{
text: "Todo 3"
}
]
})
]
First thing I suspect is the array. Try changing it to an observable.
return this.http$
.get(`http://localhost:4000/data/`)
.map(data => {
// You don't need an array because it's only 1 item
// If you want array use `Observable.from([ /* actions here */ ])`
// but then you'll need to change `map` above to
// `mergeMap` or `switchMap`
// (no big difference for this use case,
// `switchMap` is more conventional in Ngrx effects)
return new Act.GetTodoSuccess({ data: data });
})
.catch((error) => {
// You probably haven't called this yet,
// but `catch` must return `Obsrvable`
// Again, if you want an array use `Observable.from([ /* array */ ])`
return Observable.of(
new Act.GetTodoFailed({ error: error })
);
})
So I figuring out my way around Angular. Just started with a OpenWeather API based application using a simple GET method.
So here is my app.component.ts:
import { Component } from '#angular/core';
import { WeatherService } from './weather.service';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
providers: [WeatherService]
})
export class AppComponent {
title = 'Ng-Weather';
cityName: string;
constructor(private weather: WeatherService) { }
search() {
this.weather.getWeatherbyName(this.cityName);
}
}
As you can guess, the cityName variable is two way binded. The search() function is invoked onclick of a button and the data is passed to the weatherservice. The contents of weather service is:
import { Injectable } from '#angular/core';
import { Http, Response, URLSearchParams } from '#angular/http';
import { Observable } from 'rxjs';
import { Weather } from './weather';
#Injectable()
export class WeatherService {
APIurl = "http://api.openweathermap.org/data/2.5/weather";
Appid = "xxx";
constructor(private Http: Http) { }
getWeatherbyName(name: string): Observable<any> {
let myParams = new URLSearchParams();
myParams.append('q', name);
myParams.append('appid', this.Appid);
// actual http request should look like this: http://api.openweathermap.org/data/2.5/weather?appid=xxx&q=Chennai
return this.Http.get(this.APIurl, { search: myParams})
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
console.log(res.json());
let body = res.json();
return body.data;
}
private handleError(error: Response | any) {
console.error(error.message || error);
return Observable.throw(error.message || error);
}
}
But I get no error in my console or during the compile process. What is being done wrong? Also, how can I map the JSON I get to my class and give back that instance to the app.component?
Following is my class:
export class Weather {
city: String;
max_temp: String;
min_temp: String;
description: String;
}
And this is a sample JSON I receive:
{
"coord":{
"lon":80.28,
"lat":13.09
},
"weather":[
{
"id":803,
"main":"Clouds",
"description":"broken clouds",
"icon":"04n"
}
],
"base":"stations",
"main":{
"temp":304.15,
"pressure":1008,
"humidity":79,
"temp_min":304.15,
"temp_max":304.15
},
"visibility":6000,
"wind":{
"speed":3.1,
"deg":160
},
"clouds":{
"all":75
},
"dt":1504629000,
"sys":{
"type":1,
"id":7834,
"message":0.0029,
"country":"IN",
"sunrise":1504571272,
"sunset":1504615599
},
"id":1264527,
"name":"Chennai",
"cod":200
}
As you can see all I need is some data from the JSON and not the whole thing.
Your main problem here is that you are not subscribing to the observable that is being produced by your getWeatherbyName function. Observables returned by Http are cold:
Cold observables start running upon subscription, i.e., the observable sequence only starts pushing values to the observers when Subscribe is called. (…) This is different from hot observables such as mouse move events or stock tickers which are already producing values even before a subscription is active.
In order to subscribe to this observable, you can simply update your search function to the following:
search() {
this.weather.getWeatherbyName(this.cityName)
.subscribe();
}
This is by no means the complete solution to your problem - You will want to do something in the subscription, such as assign the information received to properties of your component so that they can be rendered in the UI.
You appear to have other issues in your linked project, but I suggest you ask separate questions on Stack Overflow if needed, or even better, your favorite search engine should be able to help.
Try passing a RequestOptions object to the http get instead:
import { RequestOptions } from '#angular/http';
getWeatherbyName(name: string): Observable<any> {
let myParams = new URLSearchParams();
myParams.append('q', name);
myParams.append('appid', this.Appid);
let options = new RequestOptions({ search: myParams}); //<----- NEW
// actual http request should look like this: http://api.openweathermap.org/data/2.5/weather?appid=xxx&q=Chennai
return this.Http.get(this.APIurl, options) //<<----- NEW
.map(this.extractData)
.catch(this.handleError);
}
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! :)