Assign Correctly json Object to class - json

I have a class with an interface :
from another component in the code I read a json of my list of hero, and want to create a class for each hero (so it is easier to manipulate their data)
MY shop list CLASS =>
interface ShopItemInterface {
name: string;
image: string;
price: number;
description: string;
}
export class ShopItem implements ShopItemInterface {
public name: string;
public image: string;
public price: number;
public description: string;
constructor(obj: object) {
for (let key in obj) {
// this doesn't work and I don't know why, it is always false
if (this.hasOwnProperty(key)) {
this[key] = obj[key];
}
}
}
}
LOADER COMPONENT CLASS =>
ngOnInit() {
this.http.get(this.jsonShopLocationFile).subscribe(res => {
for (let i = 0; i < res['items'].length; i++) {
this.shopItems.push(new ShopItem(res['items'][i]));
}
console.log(this.shopItems[0].name);
});
}
I can't find a way to correctly bind the json data to an object without listing all the parameters manually. ( which would be a mess and with 0 reusability)
How would you achieve that correctly ? Should I create a class and then directly call a function like hero.FromJSON(jsonObj) to manually set all the property? can I do this in some way in the constructor ?
thank you !

Because when you are constructing the object it does not have those properties, they are going to be undefined, just remove the test and it will work. Remember that interfaces are a TypeScript construct for the compiler and that you are running JavaScipt in your browser.
for (let key in obj) {
this[key] = obj[key];
}

Related

How to list objects in an object in html?

I'm writing app in Angular and Node.js. I have an object (order) that has a list of objects (items) that also contain objects (list of product id). I want to display them all in an html file. Please help me.
html file:
<div *ngIf="order">
<div *ngFor="let item of order.items"> // <- it does not work
<a>{{order.items}}</a> // <--
</div>
</div>
ts file:
export class AdminOrderItemComponent implements OnInit {
order: Order;
orderId;
constructor(private orderService: OrderService, private route: ActivatedRoute) { }
ngOnInit() {
this.route.paramMap
.subscribe(params => {
this.orderId = [params.get('id')];
this.getOrderById();
});
}
getOrderById() {
this.orderService.getOrderById(this.orderId).subscribe(
res => {
this.order = res;
},
err => console.log(err)
);
}
}
order interface:
export interface Order {
_id: any;
shipping: string;
userData: {};
sum: number;
items: {};
}
The ngFor iterates over the items of a collection. If you take a look at your model, you will realize that items is an object ({}), not an array ([]).
Your best bet is transforming the object received from your node.js backend to match your needs or (preferably I think) make your node.js model treat items as a collection as well which seems more appropriate.
Answer from #kg99:
Key: {{item.key}} and Value: {{item.value}}

Angular Map object from Get to Interface

I am getting JSON object from API.
In service I have:
getUserInfo(token: string): Observable<IUser> {
return this.http.get<any>(this.apiUrl.getUser, {headers: {'X-Auth-Token': token}}).pipe(
tap(data => console.log(data)),
catchError(this.handleError)
);
}
In component:
this.authenticationService.getUserInfo(this.token).subscribe({
next: result => {
this.user = result;
console.log(this.user);
},
error: err => console.log(err)
})
This is my interface (simplified):
export class IUser {
username: string;
email: string;
role: string;
numberOfUsers: number;
expirationDate: string;
}
Is there a way to automatically map JSON from http request to that interface, so:
If property does not exist in JSON object from request, set it to
default value, for example numberOfUsers=0 or expirationDate = null.
If there is extra property in JSON object from request, just ignore
it.
Currently the user gets overwritten with values from JSON object from request.
Is there any automatic function of Objectable that would do this? Or I have to write that method in interface?
what you describe is a class, you can't have default values in an interface.
check class-transformer, it does what you want: https://github.com/typestack/class-transformer
It can convert a plain object to a class instance and respect default values and unrelated fields:
export class IUser {
username: string;
email: string;
role: string;
numberOfUsers: number = 0;
expirationDate: string = null;
}
this.user = plainToClass(IUser, result, {
excludeExtraneousValues: true, // <- ignores keys not for the class.
});

Arrays in Angular 4 components. Unrecognized input parameters

I have a problem with the array declaration and the string interpolation in Angular 4 with TypeScript.
If I create this classes:
export class MyArrayProperty {
property1: string;
property2: string;
}
export class MyComponent {
#Input() object: ComplexObject;
myArray: MyArrayProperty[];
}
The ComplexObject is an Object with a lot of property:
ComplexObject {
myNumber: number;
myString: string;
// etc..
}
If I tried to create an array of instances of MyArrayProperty inside the component MyComponent, in this way:
export class MyComponent {
#Input() object: ComplexObject;
myArray: MyArrayProperty[] = [{
property1: 'hello',
property2: this.object.myString
}];
}
The field property1 is displayed correctly on the HTML page with the string interpolation: {{myArrayInstance.property1}} (myArrayInstance is obtained by ngFor).
But the property2 does not appear with string interpolation {{myArrayInstance.property2}}, even though this.object.myString is actually a string and I received an input object.
How does this happen and how can I solve this problem?
First of all why you're using a class for MyArrayProperty? You're just using it as a type, so you should go for an interface.
export interface MyArrayProperty {
property1: string;
property2: string;
}
Second, your code will fail because your're trying to access the property myString of the member object on component creation. But object is undefined before ngOnInit. Therefore you're accessing a property of undefined.
To solve your problem you could use a getter which transforms the object member to be displayed in your template.
export class MyComponent {
#Input()
public object: ComplexObject;
public get myArray() {
return Object.keys(this.object || {}).map(key => {
return {
property1: 'hello',
property2: this.object[key]
} as MyArrayProperty;
})
}
}
The reason for your error is that, the moment you are assigning the this.object.myString, the object is not available.
you can use ngOnChanges() which is executed everytime the #input property changes which in your case is object being inject in you component.
You can do something like this,
ngOnChanges (changes: SimpleChanges) {
if(this.object) {
myArray = [{
property1: 'hello',
property2: this.object.myString
}];
}}
To use ngOnChanges() you need to implement OnChanges interface to your component.
More on OnChanges() here
and
Angular LifeCycle Hooks

How do I pass data from a json file into a html file in Angular 2?

I have this files.
wordCloud.ts
export class HomePageComponent {
wordcloudData : Array<string>;
private searchField : string;
private wordsApi : string;
wordClouds: any[] = [];
errorMessage: string;
listId:any = 1;
#Input() data : any;
#Input() testProperty : any;
#Input() dataField : string;
#Input() apiUrl : string;
constructor(public wordCloudListService: LexiconListService) {}
getWordCloudList() {
this.wordCloudListService.get('/assets/adhoc-search.json')
.subscribe(
wordClouds => {
EmitterService.get(this.listId).emit(wordClouds);
},
error => this.errorMessage = <any>error
);
}
ngOnInit() {
this.getWordCloudList();
EmitterService.get(this.listId).subscribe((wordClouds:any) => {this.wordClouds});
}
}
wordCloud.html
<div class="center" style="margin: 0 auto; width: 30%; padding-top: 100px;">
<cst-word-cloud [data]="{{wordClouds}}"></cst-word-cloud>
</div>
As you can see, I'm trying to load a json data and display the data into the wordCloud hmtl. I'm currently having difficulties doing this? Is there anything I'm doing wrong? How do I pass the value in the wordClouds array to display it?
In your ngOnInit() you are not getting the data of wordClouds in this.wordClouds.. just do this.
ngOnInit() {
this.getWordCloudList();
EmitterService.get(this.listId)
.subscribe((wordClouds:any) => {
this.wordClouds = wordClouds;
});
}
Do not emit the data. First of all is emitting a data is not the right approach. You should always emit the states like Boolean values or data which is used for a temporary basis. I would prefer not emitting the data, instead store first. Store it in some dataStore/ Class file. Make a class and store the data in it. After storing bind the template from that class getter method.

Typescript create a class from json

I have an interface IPost and a class Post, I want to create Post from json data, in Post constructor I get the json response where the interface IPost matches it.
I mean IPost interface is generated by this great tool json2ts
json2ts : generate TypeScript interfaces from JSON
import { IPost, Title, Guid, Content, Excerpt, Embedded, Links } from './Ipost.ts';
export class Post implements IPost {
Id: number;
Date: string;
DateGmt: string;
Guid: Guid;
Modified: string;
ModifiedGmt: string;
Slug: string;
Type: string;
Link: string;
Title: Title;
Content: Content;
Excerpt: Excerpt;
Author: number;
FeaturedImage: number;
CommentStatus: string;
PingStatus: string;
Sticky: boolean;
Format: string;
Links: Links;
Embedded: Embedded;
constructor(json: any) {
var self = this;
json.subscribe(res => {
var jsonRes: any = res.json();
self = jsonRes; //something like this
});
}
}
Can I assign the class Post to json directly since json is described the same as Post class!
Is there any other ways than assigning each property to its peer from json?
Just as you would JavaScript you'll have to iterate over each value, and assign that value to self, using standard loop. Assigning to self in your case, simply changes the value of self to the json value, it doesn't make any changes this itself.
json.subscribe(res => {
let json = res.json();
for (var prop in obj) {
if( obj.hasOwnProperty( prop ) ) {
this[prop] = obj[prop];
}
}
});
NOTE: => binds this to the outer context (for example the class you're working with) to this, so that you do not need to the intermediate self variable.