Populating Class[] with JSON in TypeScript - json

As part of my model I have this class in TypeScript:
module App.Model {
export class Unit {
id: number;
participantId: number;
name: string;
isProp: boolean;
}
}
In the controller, I need a a hash with the id as key:
module App.Controllers {
export class MyController {
public units: App.Model.Unit[];
populateDemoData() {
this.units = {
"1": { "id": 1, "participantId": 1, "name": "Testname", "isProp": true },
"2": { "id": 2, "participantId": 1, "name": "Another name", "isProp": false }
};
}
}
}
However, compiling the controller, I get the following error message:
Error 2 Cannot convert '{ }; [n: number]: App.Model.Unit; }' to ' }; [n: number]: App.Model.Unit; }' is missing property 'concat' from type 'App.Model.Unit[]'.
What am I doing wrong? And why is TypeScript asking for a concat property?

You defined units as an Array object, but assigned it a literal object. Just to clarify, a hash (a literal object) is not an array.
If all the IDs are an integer you can still use the array but it would be like this instead:
populateDemoData() {
this.units = [];
this.units[1] = { "id": 1, "participantId": 1, "name": "Testname", "isProp": true };
this.units[2] = { "id": 2, "participantId": 1, "name": "Another name", "isProp": false };
}
Edit:
Ok, you have to define a hash table to do that, but you also need to make App.Model.Unit an interface that matches your JSON objects.
module App.Model {
export interface Unit {
id: number;
participantId: number;
name: string;
isProp: boolean;
}
export interface UnitHashTable {
[id: string]: Unit;
}
}
module App.Controllers {
export class MyController {
public units: App.Model.UnitHashTable;
populateDemoData() {
this.units = {
"1": { "id": 1, "participantId": 1, "name": "Testname", "isProp": true },
"2": { "id": 2, "participantId": 1, "name": "Another name", "isProp": false }
};
}
}
}

Related

Angular 13 - How to get pager details from the JSON response

I work on Angular 13 and I face an issue in that I can't retrieve pager data from the JSON.
The items array returned success but I can't return pager details.
So how to do it?
{
"items": [
{
"id": 3,
"itemNameER": "قلم",
"itemNameEN": "pen",
"description": "1"
},
{
"id": 4,
"itemNameER": "قلم",
"itemNameEN": "pencil",
"description": null
},
{
"id": 5,
"itemNameER": "قلم",
"itemNameEN": "pen2",
"description": null
},
{
"id": 8,
"itemNameER": "car",
"itemNameEN": "car",
"description": "1"
},
{
"id": 9,
"itemNameER": "mobile",
"itemNameEN": "mobile",
"description": "1"
}
],
"pager": {
"numberOfPages": 2,
"currentPage": 1,
"totalRecords": 6
}
}
What I had try is:
items?:ItemsData[];
export interface ItemsData {
id:number;
itemNameER:string;
itemNameEN:string;
description:string;
}
retrieveAllItems(pageNumber: number = 0): void {
this.erpservice.getAll(pageNumber)
.subscribe(
data => {
this.items=data.items;
console.log(data);
},
error => {
console.log(error);
});
}
How to extract pager data from JSON for the numberOfPages, currentPage and totalRecords?
Updated post
This is the information for the getAll return type.
So how to get pager data details?
export interface DataWrapper {
items: ItemsData[];
}
getAll(pageNumber: number): Observable<DataWrapper> {
let params = new HttpParams();
if (pageNumber)
params = params.append('pageNumber', pageNumber);
let httpOptions = {
params: params
};
return this.http.get<DataWrapper>(baseUrl,httpOptions);
}
What I had try is:
pager: any;
this.pager = data.pager;
But I get an error:
Property 'pager' does not exist on type 'DataWrapper'.ts(2339)
<ul>
<li *ngFor="let item of items | paginate: { currentPage:pager.currentPage }; let i = index">
{{ pager.numberOfPages * (pager.currentPage - 1) + i }}
</li>
</ul>
The error message is quite clear. The DataWrapper interface doesn't have a pager property.
You need to:
Add the pager property into DataWrapper interface.
Define the IPager interface.
export interface DataWrapper {
items: ItemsData[];
pager: IPager;
}
export interface IPager {
numberOfPages: number;
currentPage: number;
totalRecords: number;
}

How to use JSON data obtained with interface

I am trying to create an accordion from the JSON data.
I am calling the GET request from json-server and successfully able to call the API.
I am not able to access the properties of the children in the variable tileData which shows the error:
Property 'children' does not exist on type 'Tile[]'.
Not sure where I am going wrong.
tileData!: Tile[];
getTileData() {
this.tileService.showTile().subscribe((data: any) => {
this.tileData = data;
console.log('this.tileData.children.name :>> ', this.tileData.children.name ); //Shows error
});
}
The function in service file is
showTile() {
return this.http.get<Tile[]>('http://localhost:3000/data');
}
I have created an interface to store the obtained JSON data which is shown below:
export interface Tile {
name: string;
image: string;
children: { name: string; image: string; url: string };
}
My JSON data received as follows:
{
"data": [
{
"name": "First",
"image": "https://img.freepik.com/free-vector/football-2022-tournament-cup-background_206725-604.jpg?size=626&ext=jpg",
"children": [
{
"name": "Firstone",
"image": "https://img.freepik.com/free-vector/hand-painted-watercolor-abstract-watercolor-background_23-2149005675.jpg?size=626&ext=jpg",
"url": "http://www.google.com"
},
{
"name": "Firsttwo",
"image": "https://img.freepik.com/free-vector/hand-painted-watercolor-abstract-watercolor-background_23-2149005675.jpg?size=626&ext=jpg",
"url": "http://www.google.com"
},
{
"name": "Firstthree",
"image": "https://img.freepik.com/free-vector/hand-painted-watercolor-abstract-watercolor-background_23-2149005675.jpg?size=626&ext=jpg",
"url": "http://www.google.com"
}
]
},
{
"name": "Second",
"image": "https://img.freepik.com/free-vector/football-2022-tournament-cup-background_206725-604.jpg?size=626&ext=jpg",
"children": [
{
"name": "Secondone",
"image": "https://img.freepik.com/free-vector/hand-painted-watercolor-abstract-watercolor-background_23-2149005675.jpg?size=626&ext=jpg",
"url": "http://www.google.com"
},
{
"name": "Secondtwo",
"image": "https://img.freepik.com/free-vector/hand-painted-watercolor-abstract-watercolor-background_23-2149005675.jpg?size=626&ext=jpg",
"url": "http://www.google.com"
},
{
"name": "Secondthree",
"image": "https://img.freepik.com/free-vector/hand-painted-watercolor-abstract-watercolor-background_23-2149005675.jpg?size=626&ext=jpg",
"url": "http://www.google.com"
}
]
}
]
}
Issue 1
Your JSON response was an object containing the data property with Tile[].
Use map from rxjs to return as Observable<Tile[]>.
import { map } from 'rxjs';
showTile() : Observable<Tile[]> {
return this.http.get<any>('http://localhost:3000/data')
.pipe(map((response: any) => response.data));
}
Issue 2
While the children property is an array but not an object.
export interface Tile {
name: string;
image: string;
children: { name: string; image: string; url: string }[];
}
Issue 3
To print out the name, you need to iterate titleData and iterate children array from each title object.
getTileData() {
this.tileService.showTile().subscribe((data: any) => {
this.tileData = data;
for (let title of this.tileData) {
for (let child of title.children) {
console.log('title.children.name :>> ', child.name);
}
}
});
}
Demo # StackBlitz
You haven't mentioned the error but, the interface definition should use exact name as you have in data.
try
export interface Tile {
name: string;
image: string;
children: { name: string; image: string; url: string };
}

Create the correct interface for a JSON

Having a JSON that looks like this
[
{
"Background": [
{
"Blue": 16.58
},
{
"Orange": 15.39
},
{
"Yellow": 14.0
},
{
"Pink": 14.74
},
...
]
},
{
"Type": [
{
"Brown": 12.89
},
{
"Gold": 6.8
},
{
"Robot": 6.75
},
...
]
},
{
"Body": [
{
"BlackBasic": 5.15
},
{
"Bathrobe": 5.85
},
...
]
},
...
]
I would like to have an interface for my angular app. I currently have:
export interface Attributes{
[key: string]: Array<Attribute>;
}
export interface Attribute {
[key: string]: number;
}
I have a fixed set of "top" categories, Background, Type, etc. and each top category has a fixed set of values, e.g. Background has the ones shown above (and more) etc.
Is there a way to have interfaces that allow only the subvalues, depending on the "top level"?
Hint: I have control over the structure of the json as well, if there is an easier way, I could also change the json.
since you have control over the json structure, you should modify it. Is there any reason you have an array at the top level? For me it seems that object would fit better. Also for your "top categories", you could also use object instead of array. There is no need to have an array of objects when they only contain one key.
example JSON:
{
"Background": {
"Blue": 16.58,
"Orange": 15.39,
"Yellow": 14,
"Pink": 14.74
},
"Type": {
"Brown": 12.89,
"Gold": 6.8,
"Robot": 6.75
},
"Body": {
"BlackBasic": 5.15,
"Bathrobe": 5.85
}
}
example interface:
export interface Background {
Blue: number;
Orange: number;
Yellow: number;
Pink: number;
}
export interface Type {
Brown: number;
Gold: number;
Robot: number;
}
export interface Body {
BlackBasic: number;
Bathrobe: number;
}
export interface RootObject {
Background: Background;
Type: Type;
Body: Body;
}
I changed your json model cause I think it's more readable:
{
"Background": {
"Blue": 16.58,
"Orange": 15.39,
"Yellow": 14.0,
"Pink": 14.74
},
"Type": {
"Brown": 12.89,
"Gold": 6.8,
"Robot": 6.75
},
"Body": {
"BlackBasic": 5.15,
"Bathrobe": 5.85
}
}
so in this model your interface would be like:
export interface Attributes {
[key: string]: Attribute;
}
export interface Attribute {
[key: string]: number;
}

Mapping between two different Angular models

I'm trying to map Order.ts to OrderAction.ts, but getting the error Cannot ready property '0' of undefined. This error is coming up when I try to map the Order properties to OrderAction properties.
Here is Order.ts:
export class Order {
OrderId: number;
FunctionStatusList?: OrderFunctionStatus[];
}
export class OrderFunctionStatus {
FunctionTypeCode: number;
AvailableActions: OrderAvailableAction[];
}
export class OrderAvailableAction {
ActionLabel: string;
ActionValue: string;
}
Here is OrderAction.ts:
export class OrderAction {
FunctionTypeCode: number;
SelectedAction: string;
OrderList: AvailableAction[];
}
export class AvailableAction {
OrderId: number;
IsAvailableAction: boolean;
AvailableActions?: OrderAvailableAction[];
}
Here is the code that I wrote:
orders: any[] = [];
orderActionList: any[] = [];
ngOnInit() {
this.orders = this.orderService.getCheckedOrders();
this.orders.forEach((order: Order, i) => {
let orderAction: OrderAction = new OrderAction();
orderAction.OrderList[i].OrderId = order.OrderId;
orderAction.FunctionTypeCode = order.FunctionStatusList[i].FunctionTypeCode;
orderAction.AvailableActions = order.FunctionStatusList[i].AvailableActions;
orderAction.IsAvailableAction = order.FunctionStatusList[i].AvailableActions.length > 0 == true || false;
this.orderActionList.push(orderAction);
});
}
Here is a sample of the Order.ts json:
{
"OrderId": "1",
"FunctionStatusList": [{
"FunctionTypeCode": "1",
"AvailableActions": [{
"ActionLabel": "1",
"ActionValue": "1"
}]
}]
}
Here is a sample of the OrderAction.ts json:
{
"FunctionTypeCode": "1",
"SelectedAction: "1",
"OrderList": [{
"OrderId": "1",
"IsAvailableActionsLoaded": "1",
"AvailableActions": [{
"ActionLabel": "1",
"ActionValue": "1"
}]
}]
}
I'm not sure where exactly you're getting the error, but I did the following and it converts Order to OrderAction: https://plnkr.co/edit/VEHAdk3qPRIFkEAU?preview
The meat of the code is this:
this.orders.forEach((order: Order, i) => {
this.orderActions.push({
FunctionTypeCode: order.FunctionStatusList[i].FunctionTypeCode,
SelectedAction: null,
OrderList: [
{
OrderId: order.OrderId,
IsAvailableActionsLoaded:
order.FunctionStatusList[i].AvailableActions.length > 0,
AvailableActions: order.FunctionStatusList[i].AvailableActions,
},
],
});
});
I left SelectedAction as null because it was unclear how this value would be set.

JSON object interfaced return undefined

i want to create a pipe in Angular 9 that translate my table words in various language.
I have created a JSON file that contains a key and the value for a translation service. I have also created some interfaces for this json file.
translate.json
{
"lang":[
{
"IT": [
{ "name": "Nome" },
{ "surname": "Cognome" },
{ "email": "E-mail" },
{ "cf": "Codice fiscale" },
{ "phone": "Cellulare" },
{ "age": "Età" },
{ "city": "Città" },
{ "job": "Lavoro" }
]
},
{
"EN": [
{ "name": "first name" },
{ "surname": "last name" },
{ "email": "E-mail" },
{ "cf": "Fiscal code" },
{ "phone": "Phone" },
{ "age": "Age" },
{ "city": "City" },
{ "job": "Job" }
]
}
]
}
translateInterface.ts
export interface Lang {
lang: (Langs)[];
}
export interface Langs {
IT: (ITEntityOrENEntity)[];
EN: (ITEntityOrENEntity)[];
}
export interface ITEntityOrENEntity {
name: string;
surname: string;
email: string;
cf: string;
phone: string;
age: string;
city: string;
job: string;
}
translate.service.ts
translate(key: string, lang:string) {
return this.http.get<Langs>('assets/json/translate.json').subscribe((res: Lang) => console.log(res))
}
I have updated the json and the interface, now how i can return one object of the IT array?
I see multiple issues.
You don't need to define the properties IT and EN as arrays in the JSON file. It could very well be plain JS objects.
{
"IT": {
"name": "Nome",
"surname": "Cognome",
"email": "E-mail",
"cf": "Codice fiscale",
"phone": "Cellulare",
"age": "Età",
"city": "Città",
"job": "Lavoro"
},
"EN": {
"name": "first name",
"surname": "last name",
"email": "E-mail",
"cf": "Fiscal code",
"phone": "Phone",
"age": "Age",
"city": "City",
"job": "Job"
}
}
There is no need for two different interfaces. You could assign one single interface.
export interface Details {
name: string;
surname: string;
email: string;
cf: string;
phone: string;
age: string;
city: string;
job: string;
}
You are defining the type as array in Langs interface instead of the defined details interface. You could do something like
export interface Langs {
IT: Details;
EN: Details;
}
At the moment you're wrongfully returning the subscription instead of the observable from the function. You could return the observable from the service and subscribe to it the component controller.
Service
translate(language: string): Observable<Details> {
return this.http.get<Langs>('assets/json/translate.json').pipe(
map(response => response[language])
);
}
Component
ngOnInit() {
this.translateService.translate('IT').subscribe(
details => { this.details = details },
error => { }
);
}
Template
<span>{{ details?.name }}</span>
<span>{{ details?.surname }}</span>
<span>{{ details?.email }}</span>
...
Working example: Stackblitz