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.
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
How do you create a variable of type AccessGroup
declare class AccessGroup {
id: number;
active: boolean;
display_name: string;
description: string;
access_group_features: [];
created_at?: Date;
created_by?: string;
updated_at?: Date;
updated_by?: string;
static fromJSON(json: any): any;
toJSON(): this & {
access_group_features: any;
};
}
What i thought is that we can do it as such
let x:AccessGroup={
id:1,
active:false,
display_name:'',
description:'',
access_group_features:[]
toJSON(): ?
}
but it gives an error as assigning something to toJSON() is mandatory and don't know about that except the fact the & works like a intersection if I'm not wrong
Can someone give an example as to how toJSON() can be defined for a variable
You just implement it the same way as you would in a class. That means that you return an object that is based on the object running the method, plus an extra property.
That would look like { ...this, extraProp: 'whatever' }
declare class AccessGroup {
id: number;
active: boolean;
display_name: string;
toJSON(): this & {
access_group_features: any;
};
}
let x: AccessGroup = {
id: 123,
active: true,
display_name: 'Testing 123',
toJSON() {
return {
...this,
toJSON: this.toJSON, // Make typescript happy
access_group_features: 'Test'
}
}
}
And as long as you call that method like x.toJSON() then this will be the x object and everything will work correctly.
As you probably noted though, I needed to explicitly pass in the toJSON method as that wasn't getting included in ...this. I believe this is because in a class the methods are not enumerable. Methods are stored on the class prototype because they do not change per instance. this means that typescript does not believe that toJSON will be a property of { ...this }
At least I think, this one is a bit tricky.
Playground
But having any function in the return of a toJSON() probably isn't right either. So you probably don't want to include that at all.
If you change the type to something like:
toJSON(): Omit<this, 'toJSON'> & {
access_group_features: any;
};
Then you can leave out the method:
toJSON() {
return { ...this, access_group_features: 'Test' }
}
Playground
But creating a plain object to implement a class interface with methods isn't ideal. If you can just actually get that class and then do new AccessGroup() your life will probably be much easier.
One small change to what #Alex Wayne has answered above.
The original question had another property in AccessGroup called as access_group_features which is an array
declare class AccessGroup {
id: number;
active: boolean;
display_name: string;
description: string;
access_group_features: [];
created_at?: Date;
created_by?: string;
updated_at?: Date;
updated_by?: string;
static fromJSON(json: any): any;
toJSON(): this & {
access_group_features: any;
};
}
So the access_group property in toJSON() member function should also
be of the type array ( any other type throws an error as ampersand
operator in typescript intersects the properties and hence should be
of the same type)
toJSON(): this & {
access_group_features: []; /* this is probably because of strict type checking in typescript */
};
So the variable created would look something like this:
let x: AccessGroup = {
id: 123,
active: true,
display_name: 'Testing 123',
access_group_features:[],
toJSON() {
return {
...this,
toJSON: this.toJSON,
access_group_features: []
}
}
}
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.
});
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];
}
I have an interface like this:
export default interface IProject extends{
Id?:number;
name?:string;
description?:string;
}
and I when get data from the server the json file includes more properties like this:
{
id,
name,
description,
url,
startDate,
finishDate
}
but I only need the id, name and description fields. I tried this:
response.data.map((p: any) => p as IProject);
but the object includes the unnecessary data like url, startdate and finishDate
how can I map them correctly?
I know that we can map them like this:
response.data.map((p: any) => {
return {id:p.id,name:p.name,description:p.description}
});
but is there any other better ways to do that?
I'd recommend doing what you're doing, but additionally adding some types for your server response as well. That way you get some intellisense for your mapping functions.
interface IProject {
id?: number;
name?: string;
description?: string;
}
interface IProjectResponse {
id?: number;
name?: string;
description?: string;
url?: string;
startDate?: string;
finishDate?: string;
}
const mapResponse = (response: IProjectResponse[]) => response.data.map((p) => ({
id: p.id,
name:p.name,
description: p.description,
}));
const response = await fetch(/* .. */);
const data = await response.json();
const projects: IProject[] = mapResponse(data);
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.