I'm currently developing an Angular 2 application. While developing I started to use TypeScript classes to create objects from JSON I receive through HTTP or when creating a new object in a form.
The class may for example look like this.
export class Product {
public id: number;
public name: string;
public description: string;
public price: number;
private _imageId: number;
private _imageUrl: string;
constructor(obj: Object = {}) {
Object.assign(this, obj);
}
get imageId(): number {
return this._imageId;
}
set imageId(id: number) {
this._imageId = id;
this._imageUrl = `//www.example.org/images/${id}`;
}
get imageUrl(): string {
return this._imageUrl;
}
public getDTO() {
return {
name: this.name,
description: this.description,
imageId: this.imageId,
price: this.price
}
}
}
So far this solution shown above works great. But now let's assume that there are a lot more properties in the object and I want a clean DTO (for example without the private properties) for sending this Object by POST to my server. How could a more generic getDTO()
function look like? I would like to avoid having a long list of property assignment. I was thinking about using decorators for the properties. But I don't really know how to use them to filter the properties for the DTO.
You can use a property decorator for this:
const DOT_INCLUDES = {};
function DtoInclude(proto, name) {
const key = proto.constructor.name;
if (DOT_INCLUDES[key]) {
DOT_INCLUDES[key].push(name);
} else {
DOT_INCLUDES[key] = [name];
}
}
class A {
@DtoInclude
public x: number;
public y: number;
@DtoInclude
private str: string;
constructor(x: number, y: number, str: string) {
this.x = x;
this.y = y;
this.str = str;
}
toDTO(): any {
const includes: string[] = DOT_INCLUDES[(this.constructor as any).name];
const dto = {};
for (let key in this) {
if (includes.indexOf(key) >= 0) {
dto[key] = this[key];
}
}
return dto;
}
}
let a = new A(1, 2, "string");
console.log(a.toDTO()); // Object {x: 1, str: "string"}
You can use the reflect-metadata that is used in their examples if you want, I implemented it with the DOT_INCLUDES
registry so that it will work well within the playground without the need for extra dependencies.
As @Bergi commented, you can iterate over the includes
instead of this
:
toDTO(): any {
const includes: string[] = DOT_INCLUDES[(this.constructor as any).name];
const dto = {};
for (let ket of includes) {
dto[key] = this[key];
}
return dto;
}
Which is indeed more efficient and makes more sense.