Angular2 Mapping nested json array to model - arrays

I am not able to map the nested json array which is response from Web to my model array in Angular2. Suppose I have json array response as below:
[{
"base_url": "http://mysearch.net:8080/",
"date": "2016-11-09",
"lname": "MY PROJ",
"name": "HELLO",
"description": "The Test Project",
"id": 10886789,
"creationDate": null,
"version": "2.9",
"metrics": [{
"val": 11926.0,
"frmt_val": "11,926",
"key": "lines"
},
{
"val": 7893.0,
"frmt_val": "7,893",
"key": "ncloc"
}],
"key": "FFDFGDGDG"
}]
I was trying to manually map the fields referring the link Angular 2 observable doesn't 'map' to model to my model and was able to display those in my HTML by iterating through ngFor.....but I want to also display ncloc and lines value also in the HTML but I am not sure how to map those values to my Model array like mentioned in the above link.
Can you please help me with this?
Thanks.
EDIT
Mode class
export class DeiInstance {
base_url: string;
date: string;
lname : string;
name : string;
id : number;
key:string;
constructor(obj: DeiInstance) {
this.sonar_url = obj['base_url'];
this.lname = obj['lname'];
this.name = obj['name'];
this.id = obj['id'];
this.key = obj['key'];
this.date = obj['date'];
}
// New static method.
static fromJSONArray(array: Array<DeiInstance>): DeiInstance[] {
return array.map(obj => new DeiInstance(obj));
}
}

You can simplify your model and your mapping a lot.
You don't need to map your API response manually. JavaScript/TypeScript can do this for you.
First you need multiple interfaces.
export interface DeiInstance {
base_url: string;
date: string;
lname: string;
name: string;
description: string;
id: number;
creationDate: string; //probably date
version: string
metrics: Metric[];
key: string;
}
export interface Metric {
val: decimal;
frmt_val: decimal;
key: string;
}
You can then use the as-"operator" of TypeScript to cast your API response to the DeiInstance Type.
sealSearch(term: string): Observable<DeiInstance[]> {
return this.http.get(this.sealUrl + term)
.map(response => response.json() as DeiInstance[])
.catch(this.handleError);
}
If you use interfaces instead of classes you have also the advantage that you have less production code which will be sended to the client browser.
The interface is only there for pre-compile-time or however you want to call it.
Hope my code works and it solves your problem.

Related

Generate Interface from an object in Typescript

I have a Typescript project in which I want to generate an Interface from the data I have in an object, I don't know if this is possible but I can't do it.
What I do is the following:
I get the JSON Database Object with the results of a query
From the values of that object I need to create a dynamic interface to add an ArrayList to each of those values, and thus avoid modifying the interface every time the database values change
This is the object I get from database:
interface Auto {
autoType: string
}
let autoActive: Auto[] = [
{ "autoType": "Car"},
{ "autoType": "Motorcycle"},
{ "autoType": "Truck"},
{ "autoType": "Plane"}
]
console.log(autoActive.map(Object.values).toString()); // Car,Motorcycle,Truck,Plane
This is the interface I need to create from the above data (AutoBrand) from the genericObject model:
interface genericObject {
[key: string]: any;
}
interface AutoBrand extends genericObject {
"Car": Array<string>,
"Motorcycle": Array<string>,
"Truck": Array<string>,
"Plane": Array<string>
}
What I want to obtain from this is to generate a constant object to assign the titles to each final file, this is an example of the final class that I want to obtain, and then extract the titles from here:
export class HeaderConst {
static readonly headerConst: AutoBrand = {
"Car": [
"Model",
"Total",
"Age"
],
"Motorcycle": [
"Model",
"Total",
"Age"
],
"Truck": [
"Model",
"Total",
"Age"
],
"Plane": [
"Model",
"Total",
"Age"
]
}
public static getConstants(elem: string) {
return elem.split(".").reduce((elm: any, i: any) => {
return elm[i];
}, this.headerConst);
}
}
That's not possible. Your JSON only exists at runtime, and TypeScript interfaces do not exist at runtime at all.

Why is this Angular class property undefined?

I am currently working on a Angular project that makes it possible to create lobbies for different web games. The idea is that the application already gathers players so a web game can be started immediately.
But right now I am running into a problem that is caused by getting data from my Springboot Java API.
The Angular application gets the data correctly but when i try to convert the observable into a normal Game[] after subscribing ofcourse. It makes all the properties of the elements in the Game[] undefined. What is causing this to happen?
The Game Service:
//Returns a list of all games known to the API
getAllGames() :Observable<Game[]>
{
return this.httpClient.get<Game[]>(this.connectionService.getConnectionString()+"/games",this.httpOptions)
}
The Game class:
export class Game {
public Id : String;
public Name: String;
public RedirectURI : String;
public MinUserCount : Number;
public MaxUserCount : Number;
constructor(Id : String,Name : String,RedirectURI : String,MinUserCount : Number,MaxUserCount : Number)
{
this.Id = Id;
this.Name = Name;
this.RedirectURI = RedirectURI;
this.MinUserCount = MinUserCount;
this.MaxUserCount = MaxUserCount;
}
}
The Component:
games: Game[];
//Get all games known to the API
this.gameService.getAllGames().subscribe( elements => this.games = elements )
//This doesn't work all the elements of this.games are undefined.
I have also tried to work with a foreach of the array that gets returned.
With no effect either
games: Game[];
//Get all games known to the API
this.gameService.getAllGames().subscribe(elements => {
elements.forEach(item => {
var game = new Game(item.Id, item.Name, item.RedirectURI, item.MinUserCount, item.MaxUserCount)
this.games.push(game)
})
})
The JSON Result for the GetRequest
[
{
"name": "QuizGame",
"id": "dg217d65126b5e31v6d5v5d15v564d",
"maxUserCount": 4,
"redirectURI": "http://localhost:8082",
"minUserCount": 1
},
{
"name": "RPG",
"id": "dt76TQWR127367125SDYATFHa32615",
"maxUserCount": 10,
"redirectURI": "http://localhost:8083",
"minUserCount": 0
},
{
"name": "Scribble Clone",
"id": "378167e86dsahdgahkj312DJKHDg2d",
"maxUserCount": 9,
"redirectURI": "http://localhost:8084",
"minUserCount": 1
},
{
"name": "WebPonker",
"id": "0o00dhsnvkjzbcxhgjyg23v2gh3dvg",
"maxUserCount": 4,
"redirectURI": "http://localhost:8085",
"minUserCount": 4
}
]
The properties in your JSON response start with a lowercase.
In your Game class, you use properties that start with an uppercase.
I believe the parsing from JSON to a typescript object is case sensitive. Could you try to change the first letter of your properties to lowercase?

How to extract array from json on Angular

I'm working with angular2 ( version 5).
I make an http request an get back json.
I know how to access and use value but not the array.
and I don't find how to extract the two array inside element.
here my json:
{ "ImpiantiProva": [
{
"nomeImpianto":"MFL1",
"descrImpianto":"Multifilo 1",
"posizione":"Place1",
"dati_status": "true",
"unita_misura": "m/s",
"vel_attuale": 11.5,
"vel": [24.5,13.6,34.6,12.1],
"orario": ["17.05","17.06","17.07","17.08"]
},
{
"nomeImpianto":"MFL2",
"descrImpianto":"Multifilo 2",
"posizione":"Place2",
"dati_status": "true",
"unita_misura": "m/s",
"vel_attuale": 12.5,
"vel": [24.5,13.6,34.6,12.1],
"orario": ["17.05","17.06","17.07","17.08"]
}
]
}
In the data.service.ts I have the http request and it store values on :
stream$: Observable<ImpiantoModel[]>;
here my definition of the model:
#impianto.model
export class ImpiantoModel {
nomeImpianto: string;
descrImpianto: string;
posizione: string;
dati_status: string;
unita_misura: string;
vel_attuale: number;
vel: VelocitaModel[];
orario: OrariModel[];
}
#orari.model.ts
export class OrariModel {
orario: string;
}
#velocita.model.ts
export class VelocitaModel{
vel : number;
}
is it the right why to define my object?
How can I use the array "vel" and "orario"?
How can I print (access) the array "vel" of machine with "nomeImpianto" = "MFL1" ?
and how can I copy the array "vel" on new array?
thank you very much!
Here is what I understood of what you want to do : get the item in your json resp and put it in your object , so the best way is to create a static method directly when you get the json response, before returning the value create this adapter adaptImpiant(jsonObj) which will do something like :
adaptImpiant(jsonObj) {
let impiantTab = [];
jsonObj.ImpiantiProva.forEach((item) => {
let impiantoModel = {};
// impiantoModel = item if the model (below) match the item;
// if not manually set all your var like your velocita
let velocita = [] // is an array or an object with an array
// if class velocita = {}
velocita = item.vel.slice(0);
// if class velocita.valuesTab = item.vel.slice(0);
impiantoModel.velocita = velocita;
impiantTab.push(impiantoModel);
}
}
Your model seems wrong in this case, because you already use a ImpiantoModel array, so just create a class with whatever you want in :
#impianto.model
export class ImpiantoModel {
nomeImpianto: string;
descrImpianto: string;
posizione: string;
dati_status: string;
unita_misura: string;
vel_attuale: number;
vel: VelocitaModel // or simply [];
orario: OrariModel // or simply [];
}
I'm not sure I understand you, but I'll try.
is it the right why to define my object?
It should be:
export class ImpiantoModel {
nomeImpianto: string;
descrImpianto: string;
posizione: string;
dati_status: string;
unita_misura: string;
vel_attuale: number;
vel: Array<string>;
orario: Array<string>;
}
(But I have to confess, I don't know why model and not an interface)
How can I use the array "vel" and "orario"?
What do you mean?
How can I print (access) the array "vel" of machine with
"nomeImpianto" = "MFL1"
const thisContainsTheDataFromVel = whereYourDataIsStored['ImpiantiProva'].find((item) => { item['nomeImpianto'] === 'MFL1'})['vel'];
and how can I copy the array "vel" on new array?
UPDATE after reading your comment under this answer:
I took code from your example and added what you are missing. I made it so it can be more reusable (it can be enhanced even more, but I hope you understand the code and do what you need).
copyArray(data, targetValue) {
const mfl1Data = data.find((item) => item['nomeImpianto'] === targetValue);
if (mfl1Data) {
return mfl1Data.vel;
}
return [];
}
getdata2() {
this.http.get<ImpiantoModel[]>(this.myUrl)
.subscribe(
data => {
this.variableToStoreIn = this.copyArray(data, 'MFL1');
data.forEach(item => {
this.sub$.next(item);
});
});
return this.sub$;
}
CopyArray finds the data and returns it. If you don't want it like this, but just set a value of some property to the value of vel array then you can change it to:
copyArray(data) {
const mfl1Data = data.find((item) => item['nomeImpianto'] === targetValue);
if (mfl1Data) {
this.yourVariable = mfl1Data.vel;
}
}
If this answer is sufficient, please consider to mark it as the best answer, thank you.
According to your model classes, your JSON is wrong. You should have something like this:
{ "ImpiantiProva": [
{
"nomeImpianto":"MFL1",
"descrImpianto":"Multifilo 1",
"posizione":"Place1",
"dati_status": "true",
"unita_misura": "m/s",
"vel_attuale": 11.5,
"vel": [
{
"vel": 24.5
},
{
"vel": 13.6
}
...
],
"orario": [
{
"orario": "17.05"
},
{
"orario": "17.06"
}
...
]
}
]
}
Your model expects ImpiantoModel.vel and ImpiantoModel.orario to be arrays of objects. In your JSON response one is an array of numbers and the other of strings.
An if you want to use it in an HTML template, considering that you have a class attribute in your .ts file like this:
private impiantoModels: ImpiantoModel[];
You could do something like this inside your .html template:
<div *ngFor="let impModel of impiantoModels">
...
<div *ngFor="let v of impModel.vel">
<p>{{v.vel}}</p>
</div>
<div *ngFor="let o of impModel.orario">
<p>{{o.orario}}</p>
</div>
</div>

Add an array of Objects to a mutation in apollo-react

I am using react-apollo on the front-end and graphcool on the backend. I have a mutation that creates a tutorial like so:
const CREATE_TUTORIAL_MUTATION = gql`
mutation CreateTutorialMutation(
$author: String
$link: String
$title: String!
$postedById: ID!
$completed: Boolean!
) {
createTutorial(
author: $author
link: $link
title: $title
postedById: $postedById
completed: $completed
) {
author
link
title
postedBy {
id
name
}
completed
}
}
`
It gets called in a submit handler like so...
this.props.createTutorialMutation({
variables: {
author,
link,
title,
completed: false,
postedById
}
})
Everything works wonderfully.
Now I want to add a set of tags to when I create a new tutorial. I created the input field and connected it so that the tags variable is an array of objects, each with a tag id and the tag text.
If I try and add the tags field to the mutation it needs a scalar type. But there is doesn't seem to be a scalar type for an array of objects.
If I pass the tag variable in as a parameter when I call the mutation how do I fill in the Scalar type field in the mutation ( on line 148 here https://github.com/joshpitzalis/path/blob/graphQL/src/components/Add.js) and in the schema?
I am new to graphQL and I understand that I might be approaching this completely the wrong way. If that is the case, how do I add an array of objects to a mutation in graphQL?
You should add a new Tag type to your schema file and connect it to Tutorial with a new relation:
type Tutorial {
author: String
completed: Boolean
link: String
title: String!
id: ID! #isUnique
createdAt: DateTime!
updatedAt: DateTime!
postedBy: User #relation(name: "UsersTutorials")
tags: [Tag!]! #relation(name: "TutorialTags")
}
type Tag {
id: ID!
tag: String!
number: Int!
tutorials: [Tutorial!]! #relation(name: "TutorialTags")
}
Then you can create a new tutorial and new tags using a nested create mutation like this:
const CREATE_TUTORIAL_MUTATION = gql`
mutation CreateTutorialMutation(
$author: String
$link: String
$title: String!
$tags: [TutorialtagsTag!]!
$completed: Boolean!
$postedById: ID!
) {
createTutorial(
author: $author
link: $link
title: $title
tags: $tags
completed: $completed
postedById: $postedById
) {
author
link
title
postedBy {
id
name
}
completed
tags {
id
text
}
}
}
`
This post gives more background about other approaches and their trade-offs: https://www.graph.cool/forum/t/how-do-i-add-an-array-of-objects-to-a-mutation-in-apollo-react/365/6?u=nilan
What i understand by your requirement is that if you have the following code
const user = {
name:"Rohit",
age:27,
marks: [10,15],
subjects:[
{name:"maths"},
{name:"science"}
]
};
const query = `mutation {
createUser(user:${user}) {
name
}
}`
you must be getting something like
"mutation {
createUser(user:[object Object]) {
name
}
}"
instead of the expected
"mutation {
createUser(user:{
name: "Rohit" ,
age: 27 ,
marks: [10 ,15 ] ,
subjects: [
{name: "maths" } ,
{name: "science" }
]
}) {
name
}
}"
If this is what you wanted to achieve, then gqlast is a nice tag function which you can use to get the expected result
Simply grab the js file from here and use it as:
const user = {
name:"Rohit",
age:27,
marks: [10,15],
subjects:[
{name:"maths"},
{name:"science"}
]
};
const query = gqlast`mutation {
createUser(user:${user}) {
name
}
}`
The result stored in the variable query will be :
"mutation {
createUser(user:{
name: "Rohit" ,
age: 27 ,
marks: [10 ,15 ] ,
subjects: [
{name: "maths" } ,
{name: "science" }
]
}) {
name
}
}"

Map json Array in static method (Angular2+TS)

Problem to get the syntax right to map my incoming data in a static method. My json Array looks like this:
[
{
"documents": [
{
"title": "+1 (film)",
"is-saved": false,
"abstract": "some text",
"id": "_1__film_",
"url": "some url"
}
]
}
]
Each item in that array is a Result.
As an example to refer to Mapping one Result I know how to do:
static resultFromJSON(json): Result {
let documents: SearchQueryDocument[] =
json.documents.map(doc => new SearchQueryDocument(doc.title, doc.issaved, doc.abstract, doc.id, doc.url))
return new Result(documents)
}
But I need to map the whole array, so how do I do that?
static resultsFromJSON(json): Result[] {
let results: Result =
json.map ... // what here?
}
Mapping one result I can use json.documents.map... but mapping the whole array it has no "name" to use...
Maybe a stupid question from a newbie, but any help is appreciated!
If I understand you correctly then your json corresponds to the following interfaces:
interface IDocument {
title: string;
"is-saved": boolean;
"abstract": string;
id: string;
url: string;
}
interface IResult {
documents: Document[];
}
And then you have an array of Result.
To map that json you can do:
static resultsFromJSON(json): Result[] {
return json.map(obj => {
new Result(obj.documents.map(doc => {
return new SearchQueryDocument(doc.title, doc.issaved, doc.abstract, doc.id, doc.url);
}));
});
}
The solution that worked for me was just a slight change to the above static method by Nitzan Tomer.
This worked:
static resultsFromJSON(json): Result[] {
return json.map(obj =>
new Result(obj.documents.map(doc =>
new SearchQueryDocument(doc.title, doc.issaved, doc.abstract, doc.id, doc.url)
))
)
}

Resources