Angular doesn't create an object out of JSON response - json

I'm trying to create an object out of my json (from a http request) but it's a plain string.
Interfaces:
export interface CeleryTask {
uuid: string,
state: string,
received: string,
result: Chat,
}
export interface Chat {
id: number;
chatTitle: string;
chatId: string;
users: User[];
archived: boolean,
}
GET Request in my service:
loadAllSuccessTasksFromFlower(): Observable<CeleryTask[]> {
return this.http.get<CeleryTask[]>("http://localhost:5566/api/tasks?state=SUCCESS")
.pipe(map(response => Object.entries(response)
.map(entry => ({
uuid: entry[0],
state: entry[1].state,
received: entry[1].received,
result: entry[1].result
}))))
}
HTTP Response:
{
"67fe1783-4451-4fa5-838e-b78279fd5c07":{
"uuid":"67fe1783-4451-4fa5-838e-b78279fd5c07",
"name":"upload.tasks.importWorkTask",
"state":"SUCCESS",
"received":1668285215.4455156,
"sent":null,
"started":1668285219.4739492,
"rejected":null,
"succeeded":1668285419.1474545,
"failed":null,
"retried":null,
"revoked":null,
"args":"('C:\\Users\\xx\\AppData\\Local\\Temp\\xxx', 'xx.pdf')",
"kwargs":"{}",
"eta":null,
"expires":null,
"retries":0,
"result":"{'id': 9, 'chatTitle': 'My Chat'}",
"exception":null,
"timestamp":1668285419.1474545,
"runtime":199.67199999999866,
"traceback":null,
"exchange":null,
"routing_key":null,
"clock":599,
"client":null,
"root":"67fe1783-4451-4fa5-838e-b78279fd5c07",
"root_id":"67fe1783-4451-4fa5-838e-b78279fd5c07",
"parent":null,
"parent_id":null,
"children":[
],
"worker":"celery#xxx"
}
When I console.log the result:
{
"uuid": "67fe1783-4451-4fa5-838e-b78279fd5c07",
"state": "SUCCESS",
"received": 1668285215.4455156,
"result": "{'id': 9, 'chatTitle': 'My Chat'}"
}
The id & chatTitle is not a chat object, it's an plain string. So it's not possible to access object.result.chatTitle
Any idea how to solve this problem?

One way to get the object from the received string would be
result: JSON.parse(entry[1].result);
Build a type guard to let typescript understand that you really have a Chat object
function isChat(o: any): o is Chat {
// check whatever is necessary to be a Chat object
return "id" in o && "chatTitle" in o
}
Use the typeguard like this
const parsed = JSON.parse(jsonString);
if (isChat(parsed)) {
// do something with now correctly typed object
} else {
// error handling; invalid JSON format
}
See https://stackoverflow.com/a/62438143/7869582 for more info on that

Related

How to convert Incoming JSON Date String to Date in an Array of Interface

I have an interface MovieEntry and I fetch data from an API EndPoint using HttpClient that stores the returned JSON values in an Array of MovieEntry, that is, MovieEntry[]).
setMovieEntries(): void {
this.getMovieEntries()
.toPromise()
.then(data => {
this.movieEntries = data; //this line will fail to
//compile since releaseDates in data are of type string
});
}
Since the releaseDates within the JSON file are of type string it is impossible to assign the data above to this.movieEntries since it is of type MovieEntry[] where releaseDate in MovieEntry is of type Date.
I would like to learn a way to assign releaseDate from data to this.movieEntries in a way that it converts all string values of data[x].releaseDate to Date when assigning to this.movieEntries.
export interface MovieEntry {
movieName: string;
directorName: string;
releaseDate: Date; // works fine if => releaseDate: string;
}
[
{"movieName": "Fantastic Beasts and Where to Find Them",
"directorName": "David Yates",
"releaseDate": "2016-11-18T00:00:00"
},
{"movieName": "Fantastic Beasts: The Crimes of Grindelwald",
"directorName": "David Yates",
"releaseDate": "2018-11-16T00:00:00"
},
{"movieName": "Fantastic Beasts: The Secrets of Dumbledore",
"directorName": "David Yates",
"releaseDate": "2022-04-11T00:00:00"
}
]
getMovieEntries() {
return this._http.get<MovieEntry[]>(givenURL);
}

How to iterate over JSON returned by HttpClient

I have a simple Angular HttpClient, which is correctly returning JSON. I am attempting to cast the results to enforce type safety (not sure if this is correct).
But how do I actually access the returned JSON to copy it into an array?
The httpClient get() request is (and seems to be working fine):
public sendGetRequest(): Observable<Symbols[]> {
return this.httpClient.get<Symbols[]>(this.REST_API_SERVER);
}
The Symbols interface is
export interface Symbols {
code: string
desc: string
}
I have a component which calls the data service and is getting a response. However the code below returns an error when attempting to map the JSON into a string array
ERROR TypeError: syms.map is not a function
listOfOption: Array<{ value: string; label: string }> = []
this.dataService.sendGetRequest().subscribe((syms: Symbols[]) => {
console.log('return value ' + JSON.stringify(syms))
// console output shows the returned JSON and it looks correct
//this does not work, how do I copy the results to a string array??
this.listOfOption = syms.map(results => {
return {
value: results.code,
label: results.code,
}
})
})
The JSON data structure is:
{
"results": [
{
"code": "code1",
"desc": "Long description of code 1"
},
{
"code": "code2",
"desc": "Long description of code 2"
},
{
"code": "code3",
"desc": "Long description of code 3"
},
{
"code": "code4",
"desc": "Long description of code 4"
}
]
}
This is driving me crazy
Model a new interface called responseData to support response type.
export interface responseData{
results: Symbols[]
}
export interface Symbols {
code: string
desc: string
}
Update the same in service
public sendGetRequest(): Observable<responseData> {
return this.httpClient.get<responseData>(this.REST_API_SERVER);
}
You can now retrieve the results using array.map()
listOfOption: Array<{ value: string; label: string }> = []
this.dataService.sendGetRequest().subscribe((syms: responseData) => {
console.log('return value ' + syms)
this.listOfOption = syms.results.map(result => {
return {
value: result.code,
label: result.code,
}
})
})
The response data has an object root, but you're trying to parse it as an array root. I think the simplest solution would be something like this:
public sendGetRequest(): Observable<Symbols[]> {
return this.httpClient.get<{results: Symbols[]}>(this.REST_API_SERVER)
.pipe(pluck('results'));
}
Which specifies that the response data is an object with a field named results which holds an array of Symbols.
Alternatively you could also extract the response type to a separate definition:
interface ApiResponse {
results: Symbols[]
}
public sendGetRequest(): Observable<Symbols[]> {
return this.httpClient.get<ApiResponse>(this.REST_API_SERVER)
.pipe(pluck('results'));
}

best way to pass an array of object with activeroute - Angular

I'm trying to pass an array of objects through activeroute. When I pass it to the next page I get [object Object]. I saw a question on Stackoverflow where they use JSON.stringify but that didn't work for me. Or is it better to use application providers instead of queryparams.
TS of page sending the data
criteriaList: ShipmentLookupCriteria[] = [];
navigateTo() {
const navigationExtras: NavigationExtras = {
queryParams: {
criteriaList: this.criteriaList
}
};
this.router.navigate(['/lookup/results'], navigationExtras);
}
TS of page receiving the data
this.sub = this.route.queryParams.subscribe(params => {
console.log(params.criteriaList);
});
ShipmentLookUpCriteria model
import { EquipReferenceTuple } from './equip-reference-tuple.model';
export class ShipmentLookupCriteria {
terminal: string;
equipReferenceList: EquipReferenceTuple[];
constructor(terminal: string, equipReferenceList: EquipReferenceTuple[]) {
this.terminal = terminal;
this.equipReferenceList = equipReferenceList;
}
}
UPDATE
I decided to start with something simple. So I create an array of objects with dummy data.
navigateTo() {
const navigationExtras: NavigationExtras = {
queryParams: {
criteriaList: [{ name: 1, age: 1 }, { name: 2, age: 2 }]
}
};
this.router.navigate(['lookup/results'], navigationExtras);
}
PAGE RECEIVING THE PARAMS
this.route.queryParams.subscribe(params => {
console.log(params.criteriaList[0]);
});
RETURNS = [object Object] If I do again JSON.stringify it shows it as string "[object Object]". if I do params.criteriaList[0].name returns undefined
You can simply pass,
this.router.navigate(['/lookup/results'], {queryParams: {criteriaList: this.criteriaList }});
and access it using
this.sub = this.route.snapshot.queryParamMap.get('criteriaList');

Unable to get object properties from http get method in angular 4

I have two classes defined as follows,
export class Cycle {
lock_id: number;
bicycle_id: number;
qr_code: number;
latitude: number;
longitude: number;
status: number;
battery_level: number;
is_locked: number;
is_active: boolean;
last_date_updated_on: Date;
last_location_updated_on: Date;
constructor () {
}
}
And another class is,
import { Cycle } from './Cycle';
export class ParkingLocation {
parking_id: number;
latitude: number;
longitude: number;
parking_radius: number;
max_cycle_limit: number;
cycles: Cycle[];
available_parking_space: number;
available_cycles: number;
address: string;
area_id: number;
constructor () {}
}
I am getting a JSON object from http get method which is called as follows,
return this.http.get(this.serverUrl + this.getParkingAreaUrl + '?parkingArea=' + this.myWebLocation.id, httpOptions)
.subscribe( (data: ParkingLocation[]) => {
// console.log(data);
this.location = data;
console.log(this.location);
for ( let obj of this.location) {
console.log(obj.parking_id);
console.log(obj.address);
console.log(obj.latitude);
console.log(obj.cycles);
}
},
(err: HttpErrorResponse) => {
if (err.error instanceof Error) {
console.log('Client-side error occured.');
} else {
console.log('Server-side error occured.');
console.log( err );
}
}
);
}
And here is JSON output from the method,
[
{
"parkingId": 1,
"latitude": 12.958042,
"longitude": 77.716313,
"parkingRadius": 1,
"maxCycleLimit": 5,
"cycles": [
{
"lockId": "123654789123655",
"bicycleId": 0,
"latitude": 12.955596,
"longitude": 77.714446,
"status": 3,
"batteryLevel": 500,
"parking": {
"parkingId": 1,
"latitude": 12.958042,
"longitude": 77.716313,
"parkingRadius": 1,
"maxCycleLimit": 5,
"cycles": null,
"avilableParkingSpace": 0,
"availableCycles": 0,
"address": "HyperCity Market"
},
"qrCode": "1123",
"lastDataUpdatedOn": null,
"lastLocationUpdatedOn": null,
"active": true,
"locked": false
}
],
"avilableParkingSpace": 0,
"availableCycles": 0,
"address": "HyperCity Market",
"areaId": 1
}
]
But when i ran the application, the console statements written while calling http method is giving output
Undefined
For attributes like parking_id, cycles, but the correct output is given for other 2 attributes i.e for address and latitude.
Please correct me where I am wrong, the JSON output is mapping correctly to location object? If not, where I am going wrong, Please suggest me some articles to read for the problem.
NOTE: I am unable to follow, what .map(),.subscribe() function will do to the http.get() method in angular4.
What you are doing now, is not creating instances of you class, with
.subscribe( (data: ParkingLocation[]) => {
you are just saying that what you expect is a response that matches your class. You need to explicitly map your response to your class. There are several ways to do that. You could do it longhand..
// assuming you are using HttpClient:
.map(response => response.map(x => new ParkingLocation(x.parkingId /** ....**/))
But it would be easier to create some kind of serializer or create a class constructor. Here are some ideas for that: How do I cast a JSON object to a typescript class and this answer.

Typescript JSON deserialize to interface missing methods

I have a set of classes with a fairly straightforward set of fields:
interface IQuiz {
name: string;
questions: Question[];
score(): number;
}
export class Quiz implements IQuiz {
name: string;
questions: Question[];
score(): number {
var theScore: number = 0;
this.questions.forEach(question => (theScore += question.value));
return theScore;
}
}
interface IQuestion {
text: string;
value: number;
}
export class Question {
text: string;
value: number;
}
Then I have some JSON which represents some instances of those objects. This JSON includes values for Quiz.name, an array of Quiz.questions each with values for their text and value fields.
Somewhere else, I have a reference to this Quiz class that has been created from JSON.parse(json) like the code below:
var json = '{"name": "quizA", "questions": [{"text": "Question A", "value": 0 }, ... ]}'
var quiz = <IQuiz>JSON.parse(json);
// Some other stuff happens which assigns values to each question
var score = quiz.score(); // quiz.score is not a function
It seems the deserialized object is not actually an implementation of the interface provided.
How do I get a reference to an instance of the correct interface, so that I can call quiz.score() and it actually call the method?
There is no need of interfaces, it will add twice the work for maintaining your model's properties. Add a constructor in your Quiz class so that it extends your json object.
export class Quiz {
constructor( json: any )
{
$.extend(this, json);
// the same principle applies to questions:
if (json.questions)
this.questions = $.map( json.questions, (q) => { return new Question( q ); } );
}
name: string;
questions: Question[];
score(): number { ... }
}
In your ajax callback, create the Quiz object from your json Object:
let newQuiz = new Quiz( jsonQuiz );
let score = newQuiz.score();
I detailed the solution in that post.