How can I parse JSON attributes with slashes properly? - json

I have 2 Interfaces:
DTO.ts
export interface Report{
berichtId: number,
summary: Label
}
export interface Label{
text: string
}
I use them to type my HttpClient get request, to receive an array of Reports:
Dao.service.ts
getReports():Observable<DTO.Report[]>{
return this.http.get<DTO.Report[]>(Dao.API+'report/current')
.pipe(
retry(3),
catchError(this.handleErrors)
);
What I get is this JSON:
[
{
"berichtId":1777,
"summary":
"{\"text\":\"asdf\"}"
}
]
Now I want to read them out but these slashes dont let me convert the JSON to my pre defined interface Report object. It just translates it to a normal string.
this.dao.getReports().subscribe(report=>{
console.log(report[0].summary); // {"text":"asdf"}
console.log(report[0].summary.text) // undefined
});
What is the best way to handle that problem? There are solutions online but they are often rather counter intuitive. There must be a better way in Angular.

I think you need to use map operator from rxjs/operators before returniing data from service.
I have intercepted the data using map (rxjs) and then parsed the summary.
Demo (check console on button click)
Service
import { Injectable } from '#angular/core';
import { of, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { Report } from './types';
import { HttpClient } from '#angular/common/http';
#Injectable()
export class DataService {
constructor(private http: HttpClient) { }
getReports(): Observable<Report[]> {
return this.http.get<any[]>('api')
.pipe(
map(
values =>
values.map(val => {
val.summary = JSON.parse(val.summary)
return val;
}
)
));
}
// mock methos for demo
mockGetReports() {
return of<any[]>([
{
"berichtId":1777,
"summary":
"{\"text\":\"asdf\"}"
}
])
.pipe(
map(
values =>
values.map(val => {
val.summary = JSON.parse(val.summary)
return val;
}
)
));
}
}

I think you should try JSON.parse() or if it does not help then you can try this npm library
I hope this solves your issue.

Related

Angular observable subscribe JSON parsing issue

I have a service performing http.get on a Drupal API and retrieving JSON data.
The component utilising that JSON data keeps generating the following error:
ERROR in src/app/form-test/form-test.component.ts(18,28): error TS2551: Property 'included' does not exist on type 'Question[]'. Did you mean 'includes'?
From the following code:
constructor(private dataService: QuizService) { }
ngOnInit() {
this.dataService.fetch().subscribe(data => {
this.jsondata = data.included[0].attributes.field_json;
console.log(data, ': DATA');
});
}
I don't understand why there is a problem with the JSON and why it's trying to find includes instead of included in the JSON structure. Below is a screenshot of a sample of the JSON:
I have confirmed the structure of the JSON data (as confirmed from the image above), also from console logging the JSON data and that the API URL is live at the time Ay angular app is attempting to call it.
Can anyone advice what is the cause of this error and how can I resolve it?
UPDATE:
quiz.service.ts:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Observable } from 'rxjs';
export interface Question {
// title: string;
question: string;
included: any[];
}
#Injectable({
providedIn: 'root'
})
export class QuizService {
// tslint:disable-next-line: max-line-length
private quizURL: string = 'http://drupal-8-composer-drupal-test.com/jsonapi/node/quiz/31f020f7-34d9-4b9a-bd2b-0d567eb285dc/?include=field_questions&fields%5Bnode--quiz%5D=title,drupal_internal__nid,body&fields%5Bnode--question%5D=title,field_processed,body,field_options,field_json';
constructor(private httpClient: HttpClient) { }
fetch(): Observable<Question[]> {
return this.httpClient.get<Question[]>( this.quizURL );
}
}
The error states that data has type Question[]. It is an array, not an object. Typescript compiler tries to find an included variable in array and there's none. So it gives you an error.
Your JSON structure contains an array of questions in the included field. So the type which the fetch returns should be like { included: Question[] }:
fetch(): Observable<{ included: Question[] }> {
return this.httpClient.get<{ included: Question[] }>( this.quizURL );
}
Or you can process the response in service and return questions only:
fetch(): Observable<Question[]> {
return this.httpClient.get(this.quizURL)
.pipe(map((data: { included: Question[] }) => data.included));
}
.map operator gets the whole response object, extracts only questions and returns them as array.

Parse a json data from internal json file using Angular throwns error

I tried to get json from tne internal json file within angular.
with this service (village.service):
import { Injectable, OnInit } from '#angular/core';
import { Http, Response } from '#angular/http';
import { environment } from '../../environments/environment';
import { Observable } from 'rxjs'
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
#Injectable()
export class RecordsService {
data: any;
constructor(private http: Http) { }
getVillages(id) {
return this.http.get('../assets/data/villages.json')
.map(data => {
this.data = data.json();
return data.json();
}, err => {
if (err) {
return err.json();
}
});
}
}
and under commponet i put the:
ngOnInit() {
this.getVillages();
....
}
and here to load as the chain dropdown
onSubDistrictSelected(subDistrictId: number) {
if (subDistrictId) {
this.onLoading.emit(true);
this.customer.subDistrict = this.subDistricts.filter(c => (c.id == subDistrictId))[0].name;
this.customer.sdid = subDistrictId;
this.customer.subDistrictId = subDistrictId;
this.villages = this.getVillages().filter((item) => {
return item.subDistrictId === Number(subDistrictId)
});
this.onLoading.emit(false);
}
}
I got error when compile said: this.getVillages is not function, But is working correctly if i put the json value inside the component file:
getVillages() {
return [
{ json_data}
]
}
What I want to achieved is I want to used the JSon file instead put directly inside the commponet.
Thanks,
getVillages is a method in service, so you need to instantiate the service before you use it.
First you need to provide the RecordsService in a module, like,
app.module.ts
...
providers : [
RecordsService
]
...
And in your component,
abc.component.ts
constructor(public recordService : RecordsService) {
}
ngOnInit() {
this.recordService.getVillages();
}
Let me know if you still get the error or have some different error.
EDIT:
getVillages() is returning an Observable, so you need to subscribe in order to use the data returned.
this.recordService.getVillages().subscribe( data => {
console.log(data);
} )

Property 'locations' does not exist on type 'Object'

import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
import 'rxjs/add/operator/map';
#Injectable()
export class LocationsProvider {
data: any;
constructor(public http: HttpClient) {
}
load() {
if (this.data) {
return Promise.resolve(this.data);
}
return new Promise(resolve => {
this.http.get('assets/data/locations.json').subscribe(data => {
this.data = this.applyHaversine(data.locations);
this.data.sort((locationA, locationB) => {
return locationA.distance - locationB.distance;
});
resolve(this.data);
});
});
}
enter image description here
i am pretty new here, and pretty new to ionic, i'll probably requires detailed solution, i cant seems to make ionic read a json file
You are getting a compile time error in data.locations specifically locations is not defined on the data property.
Fix
Tell TypeScript that it is e.g. use an assertion:
this.data = this.applyHaversine((data as any).locations);
If you know the type of your response, you can add a generic to http.get<T>() to type data.
interface SomeInterface {
locations: Location[]
}
this.http.get('assets/data/locations.json')<SomeInterface>.subscribe(data => {
this.data = this.applyHaversine(data.locations);
...
});
or if you don't want to create an interface for it (not recommended)
this.http.get('assets/data/locations.json')<SomeInterface>.subscribe((data: any) => {
this.data = this.applyHaversine(data.locations);
...
});

Making ngrx-effects REST call

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

Angular doesn't pass HTTP GET params properly

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