Yehia A.Salam
Yehia A.Salam

Reputation: 2078

Property Decorators in Angular Model

I have an Angular 8 application that retrieves some data from the backend and displays it on the front end. I have a problem that the backend model and the frontend model is not exactly the same, for example the backend model has a date field in SQL format, and on the frontend i want it in javascript friendly format.

So I thought of creating a decorator for the date property, instead of creating another property in the class and mapping it with the right value. So to visualize:

Method #1: Not so clever approach: Introduce a new created property with the right date format:

export class Message {
    id: number;
    message: string;
    visitor: Visitor;

    createdAt: string; /* Holds the backend model created date */
    created: Date; /* Holds the frontend javscript date */
}

/* API Call in Service */

  public getMessages(visitor_id: number) : Observable<Messages>  {
    return this.httpClient.get<Messages>(`${API_URL}/api/SampleData/Messages?visitor=${visitor_id}`).pipe(

      map(v => {
        v.model.map(i => {
          i.created = moment(i.createdAt.replace('T', ' '), 'YYYY-MM-DD HH:mm:ss').toDate() ;
          return i;
        })
        return v;
      })

    );
  }

Method #2: Neat Approach Using Property Decorators:


export class Message {
    id: number;
    message: string;
    visitor: Visitor;

    @DateTransform()
    createdAt: string;
}

function DateTransform() {
  return function (target: any, key: string) {
    Object.defineProperty(target, key, { 
      configurable: false,
      get: () => {
        console.log('trying to get value:' + key); /* This line doesnt fire */
        return moment(key.replace('T', ' '), 'YYYY-MM-DD HH:mm:ss').toDate() 
      }
    });
  }
}

/* And in the component html */

<span class="kt-chat__datetime">{{ message.createdAt | amTimeAgo }}</span>

So the second approach looks like the right one, however, the getter function is completely ignored, and the component template still tries to render the old value. So my question is,

  1. What might cause the getter not being fired for everything to work,
  2. Can the getter returns a different type (date) instead of the original string:
  3. And most importantly, is decorators the right approach here?

Thanks

Upvotes: 1

Views: 2319

Answers (2)

Mukesh Ranjan
Mukesh Ranjan

Reputation: 165

You can use typescript decorators for your problem;

I think your question is already answered, please checkout this. How to Create a Simple Typescript Metadata Annotation

Upvotes: 1

tftd
tftd

Reputation: 17032

Instead of doing this manually, you could use the class-transformer library and convert the received JSON into an actual object automatically. It also allows you to specify a @Transform annotation/decorator to further customize how the plain value is transformed.

Here's an example:

import {plainToClass} from "class-transformer";

class User {
    id: number;
    firstName: string;
    lastName: string;

    @Type(() => Date)
    @Transform(value => moment(value), { toClassOnly: true })
    date: Moment;
}

const fromPlainUser = {
    unkownProp: 'hello there',
    firstName: 'Umed',
    lastName: 'Khudoiberdiev',
    date: '2013-02-08 09:30 '
}

console.log(plainToClass(User, fromPlainUser))

// User {
//   unkownProp: 'hello there',
//   firstName: 'Umed',
//   lastName: 'Khudoiberdiev',
//   date: Date Object
// }

You can checkout the README section of the library - it has multiple examples of how to use it.

Upvotes: 0

Related Questions