Another use-case for an Angular decorator.

Andrei Mihalciuc
2 min readMar 2, 2018

Decorators in the TypeScript is very powerful concept. They provide a way to apply Aspect Oriented Programming to your Angular application. They allow to extract cross-cutting concerns and apply them without modifying the existing code.

Source

Basically a decorator is just a function which allows to execute a piece of code before and/or after the target function. Netanel Basal has a very good article explaining the basics of the decorators. In this short post I’d like to show another example where decorators could be useful.

The use case. We had an existing REST API, that accepts dates as strings in the format of DD-MM-YYYY, meaning that we have to convert dates to strings before each service call. The decorator implementation gives us the following benefits: no changes to the code; can be applied in the required places only; if the format changes, only the decorator needs to be updated. Expected usage:

@ConvertDates
list(params: YourModel): Observable<any> {
return this.http.post<YourResponse>(url, params);
}

The complete implementation can be found on the Plunker. Here is the essential code for the decorator:

export function ConvertDates(target, key, descriptor) {
// save a reference to the original method
const originalMethod = descriptor.value;
descriptor.value = function (...args) {
return originalMethod.apply(
this,
// iterate over list of arguments and format them
args.map(formatArgument)
);
};
return descriptor;
}

Here we just save the original function and then change it’s descriptor with the updated input parameters args.map(formatArgument). That’s it, our decorator is ready.

Let’s now finish the implementation (I’m using some functional programming concepts from an amazing article written by Eric Elliott):

// if a parameter is an object, iterate over date properties
// and convert to api format
// otherwise return parameter as is
const formatArgument = (arg) => isObject(arg)
? Object.assign({}, arg, formatDateProps(arg))
: arg;

// pipe executes functions passing result of the previous function
// as an input to the next
const formatDateProps = pipe(
// break object to key-value pairs
toKeyValue,
// filter out date properties only
filter(dates),
// convert dates to api format
map(toApiDate),
// convert properties back to an object
convert(propsToObject)
);

And the rest of the functions:

const isObject = (obj) => obj && typeof obj === 'object';
const toKeyValue = (obj) => Object.keys(obj).map(key => ([key, obj[key]]));
const dates = ([key, value]) => value && value instanceof Date;
const toApiDate = ([key, value]) => ([key, formatDate(value)]);
const propsToObject = (src, [key, value]) => Object.assign(src, { [key]: value });
const convert = (fn) => reduce(fn, {});
function formatDate(date: Date): string {
return (date.getMonth() + 1) + "/" + date.getDate() + "/" + date.getFullYear();
}

Conclusion

As you saw decorators are a great way to add logic to your code. The output code remains clean and maintainable. Hope you enjoy it and come up with another creative way of using them.

--

--