0

I started developing with angular / typescript and created a service for my .NET Core API and I like to know what's the best way to get a clean and reliable object from my service.

I have an .NET CORE REST API returning a json result represented by the class definition below.

Service: demoservice.getDemo().subscribe((val) => new Demo(val));

Demo-Class is the following code:

export class Demo  {
    public id : number;
    public name : string;
    public subDemo: SubDemo;

    constructor(demo: Demo) {
      this.id = demo.id;
      this.name = demo.name;
      this.subDemo = new SubDemo(demo.subDemo);
    }
}

export class SubDemo {
    public demoList : ListDemo[]

    constructor(subDemo: SubDemo) {
        this.demoList = new Array<ListDemo>();
        subDemo.demoList.forEach(dl => {
            this.demoList.push(new ListDemo(dl))
        });
    }
}

export class ListDemo {
    constructor(listdemo : ListDemo) {
        this.birthday = listdemo.birthday;
        this.smoker = listdemo.smoker;    
    }

    get birthDayFormatted() : Date {
        return new Date(this.birthday);
    }

    public birthday : string;
    public smoker : boolean;
}

I this the best way (full implement all constructors) to create a object. Please note I like to use the "getter" - functionality of my ListDemo Class.

Is there no better way? I just found some Object.clone / Object.assign / Object.create.

But none of this solution is comprehensive...

I am really interested in your experience..

Cœur
  • 37,241
  • 25
  • 195
  • 267
Andréw
  • 96
  • 1
  • 11
  • "*I this the best way (full implement all constructors) to create a object*" best how? As a side note, all your constructors are copy-constructors. So, to instantiate an object, you need to have an instance of that object. Seems like you can never create one because of a chicken and egg problem. – VLAZ May 14 '19 at 19:18
  • I have a kind of object. I know that.. The object returned by my service.. But its completely anonmyous.. And this object does not have my functions implemented.. – Andréw May 14 '19 at 19:27
  • If you are sure your API is adhering to a proper schema (using tests for example or runtime validation using `io-ts` for example) you can just drop the constructors entirely and write something akin to `JSON.parse(apiResponse) as Demo`. – Christian Ivicevic May 14 '19 at 22:32

2 Answers2

2

Since you're using better & best I will answer with my, probably unwanted, opinion. Disclaimer: I'm no guru, this answer is based on opinion, feel free to disregard.

Don't do it. Your server has a set of objects in its domain, probably some kind of problem solving or data storage domain.

Your client has a set of objects in its domain, typically focused on presenting the data to the user and allowing the user to understand and manipulate it.

Both of these domains may have objects that have the same name or are based on the same real world concept. It can be tempting to feel like they are the same domain with the same objects. They are not. If they were the same you would not be writing a client and a server you would be writing two of the same thing. The two should communicate with pure data objects. In TS this means you should only create an interface, not a class, for the objects you receive from the server.

Instead, start over. Create a new domain based on what you want to appear in the API. If you design your API from the top (UI) down to the bottom (access services) you'll likely find that you don't have a one-to-one mapping between objects. On the occasions you do then you can probably get away with the occasional assign / merge (see below) but I've personally never found reason to do more than what you posted above.

Should you persist you may come across the option to reassign the prototype of the JSON literal from the base object to the class the data represents but that is a contentious topic and potential performance pitfall.. Your best bet is probably to just do a recursive/deep assign like Lodash's merge.

Pace
  • 41,875
  • 13
  • 113
  • 156
  • Thank you for your explanations! I completely agree with you too. But for me it makes sense to get a formatted Date(); for my UI (Angular). And since it is ONLY for the UI, I don't want to change the server object. – Andréw May 15 '19 at 07:03
1

Just use interfaces not classes.

export interface Demo  {
  id: number;
  name: string;
  subDemo: SubDemo;
}

export interface SubDemo {
  demoList: ListDemo[];
}

export interface ListDemo {
    birthday: string;
    smoker: boolean;
}

and your api should return the same shape, you should just be able to go

getDemo(): Observable<Demo> {
  return this.http.get<Demo>('url');
}

in your service and in your component assign it to a property

demo$ = this.service.getDemo();

and then use the observable with the async pipe in your template.

<ng-container *ngIf="demo$ | async as demo">
  {{ demo | json }}
</ng-container>

The less you have to manipulate your data the better. I have a VS pluging that allows you to paste C# classes into TS files and it converts them to TypeScript interfaces on the fly.

Adrian Brand
  • 20,384
  • 4
  • 39
  • 60
  • Can you give an opinion about the topic "Date()"? I get a DateTime (C#) from the service and want to add it to a Kendo Timepicker. He tells me that the value is wrong. Why, I already know, but how would you now "manipulate" this value so that I can use it? I can't use a "getter" in the case of an interface. So I would solve it in C#. – Andréw May 15 '19 at 06:43
  • Dates in JSON are a pain because there is no native date in JSON. I do it with a map. this.http.get('url').pipe(map(val => ({...val, birthday: new Date(val.birthday)}))) – Adrian Brand May 15 '19 at 09:03