4

I have a situation where I have defined three model classes as below

export class Parent 
{
    name: string;
    containers: Container[];  
}

export class Container
{
    desc: string;
    sets: Sets[];  
}


export class Sets
{
    private _data?: any[];
    private _dataSet?: any[];

    get data() 
    {
        if (this.dataSet && this.dataSet.length > 0) 
        {
            return this._dataSet[0].data;
        }
        return this._data;
    }

    set data(data: any[]) 
    {
        this._data = data;
    }

    get dataSet() 
    {
        return this._dataSet;
    }

    set dataSet(dataSet: any[]) 
    {
        this._dataSet = dataSet;
    }
}

The problem is: the getter and setter method of Set class wouldn't get triggered unless I do new Set() explicitly (I don't know if the new Set() this is really necessary).

For example:

"name": "vik",
"containers": [
{
    "desc": "something",
    "sets": [
    {
        "dataSet": [
        {
            "data":[
            {
                // Object
            }]
        }]
    }]
}]

When i do:

let response:Parent = responseObj;
console.log(response.containers[0].sets[0].data)

it should return the DataSet value, but instead it returns undefined.

But if i do:

let response:Parent = responseObj;
console.log(new Sets(response.containers[0].sets[0]).data)

returns me the correct value.

Did I miss something or how could I improve my code so it works?

Daniel
  • 3,541
  • 3
  • 33
  • 46
Vikhyath Maiya
  • 3,122
  • 3
  • 34
  • 68

2 Answers2

1

The problem is that your object comes from HTTP. What does this mean:

Http is a text based format. This means that when response comes to you it is an only string like:

"{"foo":"boor"}"

Then you do smth like JSON.parse(data) and return real object like:

{foo: "bar"};

But JSON.parse does no nothing about type you expected. It doesn't know how to make from string instance of the object. It can just try to make a simple object and this is all.

You see, your data object has no information about it's type, methods, getter/setters e.t.c. It is just about plane data aka dto. So, if you want to get real classes from your dto you should write smarter parser, that will take your dto and create from it real instance of the expected class. Basically you already do this by

new Sets(response.containers[0].sets[0])

So you can keep doing this, but than you will find that this repetitive code is all around your application. The best way I found is to inject mapper to the data server and return not dto but an instance. So your other code will no nothing about this "dirty" work and will always works with "normal" objects.

Hope this helps.

UPD:

const rawData = '{"name":"Vitalii"}';
const person1 = JSON.parse(rawData);

function Person({name}) {
  this.name = name;
  this.say = function() {
    console.log(`Hi, ${this.name}`);
  }
}

let person2 = new Person(person1);
person2.say();

// person1 and person2 have the same data inside. But: 

console.log(person2 instanceof Person); // true
console.log(person1 instanceof Person); // false

This is because this object have a different nature even if they looks similar.

Drag13
  • 5,859
  • 1
  • 18
  • 42
  • But shouldnt it work when i do , new Parent(response) ?? – Vikhyath Maiya Nov 27 '17 at 14:24
  • It depends on what logic you will put into your constructor. From your response you can get only data and this is all. Then you have to create new object for each object you have in your tree that has methods. :( Sorry, I didn't see anything that can do this work instead of you or me. – Drag13 Nov 27 '17 at 14:29
  • You should understand that there is a huge and deep whole between your classes and raw data you get from JSON.parse or such other things. – Drag13 Nov 27 '17 at 14:36
  • So just to make the point clear, If i have an object which has custom types inside, then each of the custom types has to be instantiated with new keyword to run setters and getters ...am i right or i am understanding it wrong ? – Vikhyath Maiya Nov 27 '17 at 14:36
  • If in your class you have anything except fields (getters, setters, static fields, functions) - yes. I will add small example may be this make my explanation a bit clearer. – Drag13 Nov 27 '17 at 14:39
1

Yes, you have to (somehow) parse your plain object to class instance you defined, others has already answered it for you.

So I just suggest one way of refactoring: nested object of class transformer

import { Type, plainToClass } from 'class-transformer';

export class Parent {
    @Type(() => Container)
    name: string;
    containers: Container[] = [];  
    static fromJson(json: any): Parent {
      return plainToClass<any, Parent>(Parent, json);
    }
}

export class Container{
    @Type(() => Set)
    desc: string;
    sets: Set[] = [];
}

export class Set{
    private _data?: any[];
    private _dataSet?: any[];

    //your code
}

and then

let response:Parent = responseObj;
console.log(Parent.fromJson(response).containers[0].sets[0].data)

this is much neater, I think

hope this help~

Kapuzzi
  • 98
  • 8