Achieve A Better Angular Application Using TypeScript Decorators

Both Angular and TS are great tools. Using both helps us achieve a great application. Combining the advantages of TS features together with Agular’s advanced functionality can leverage your app even more.

One of the advanced features TS has to offer is the Decorator. Combining Decorators together with Angular can make your application cleaner, more readable and can reduce code duplications.

So what are TS decorators anyway?

Official defenition-

A Decorator is a special kind of declaration that can be attached to a class declaration, method, accessor, property, or parameter. Decorators use the form , where must evaluate to a function that will be called at runtime with information about the decorated declaration.

Unofficial defenition -

Decorator is a simple function that wraps and give us the ability to manipulate our function (At least for me it was the easiest way to understand this feature 😀).

Decorators can be applyed on a — Class |Method | Accessor | Property | Parameter.

Let’s take a look on some examples in order to get some intuition on how decorators works.

Below is a class with one simple method that prints a sum of three numbers to a console -

Imagine that during the development proccess we want to print all the given arguments to the console, to achive this, we need a function which will receive our function as argument and iterate over all given arguments in order to print them to the console.

We will implement decorator that will look like a below -

And the decorator will be applyed like this → on top of our function

When decorator function is applayed, three arguments passed to the decorator function -

(An object which represents a class that our method is member of)

(A string representing our method name)

(PropertyDescriptor is an object that contains the actual method logic)

Now we can add some code to our decorator function -

Ok, so after we’ve put some logic we can rerun our code and take another look at the console…

As we can see, we are now able to print the given arguments without messing up our function! 🤩

Let’s take it one step further -

How we can pass a property to our decorator? for example- we want to pass an argument to our decorator which indicates the log level we are interested in (info, warning or error?)

In this case we will need to use a pattern called Decorator Factory -

If we want to customize how a decorator is applied to a declaration, we can write a decorator factory. A Decorator Factory is simply a function that returns the expression that will be called by the decorator at runtime.

lets apply this pattern on our decorator -

As we can see our decorator can now recieve parameters 😀

Ok, now that we have some intuition on how decorators work, we are ready to combine decorators with some Angular functionality —

First thing first — To enable experimental support for decorators, you must enable the compiler option either on the command line or in your :

Command Line:

tsconfig.json:

@RunOutsideAngular Decorator

One of the optimization techniques in Angular is to run functions outside Angular zone.

Running functions via runOutsideAngular allows you to escape Angular’s zone and do work that doesn’t trigger Angular change-detection (angular.io).

Below is a simple function which simulate a healthCheck function, this function runs every 1 sec.

Running the code above will trigger change detection every sec.

As we can see from the console output, and the text on the screen, after execution of healthCheck function, Angular does performing change detection.

How can we get rid of this redundant change detection cycle? Run healthCheck outside Angular!

Running code above will not trigger change detection every sec.

As we can see from the console output, and the text on the screen, after execution of healthCheck function, Angular does not perform change detection.

Ok, but do we need to wrap each time our functions with this runOutsideAngular code? No! lets make it simple!

let’s write another decorator -

Applaying this decorator on our function will achive the same functionality but in a much cleaner way, in addition we can use this decorator on every function that doesn’t need to run inside Angular and run redundant change detection cycles.

@Memoization Decorator

In computing, memoization or memoisation is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again (Wikipedia)

Below we have an example of a ‘heavy’ and stateless function, in this case we will use fibonacci to simulate some ‘heavy’ calculation that can cause bad performance to our application.

Running the fibonacci function twice with the same input without using memoization is not smart…below is a result of this run -

As we can see fibonacci function runs twise. Each run took ~16 sec.

Can we do better? Of course! We can memoaize our function, in order to avoid redundant calculations.

Now we will apply Memoization decorator on our ‘heavy’ function…

let’s run this once again and take another look at the console -

As we can see — the first ‘‘heavy’’ function execution took ~16 sec while the second took less than 1 sec!

The second (and more interesting, IMO) way to use decorator is to apply it on a Class, it is a similar to method decorator, it is like a method decorator, but in the case of a class, we’re exposed to all class members.

When decorator function is applayed on a class, only one argument passed to the decorator function

(An object which represents a whole class)

This time we will implement decorator which will help us to recognize changes in collection like objects while we use ChangeDetectionStrategy.OnPush

Using ChangeDetectionStrategy.OnPush is a great way to optimize angular components, for a comprehensive guide please read this great article →

A Comprehensive Guide to Angular onPush Change Detection Strategy by Netanel Basal)

The case — We have two components, parent and child component. Child component is set to ChangeDetectionStrategy.OnPush meaning that the change detection on a child component will run only when our @Input referance changed.

At this point everyting works as expected, we can click on a button in ParentComponent which pushes a new item into an arrayOfSmiles, and finaly the ChildComponent rerendering the Input() arrayOfSmiles.

But what will happen if you will change the ChildComponent from ChangeDetectionStrategy.Default to ChangeDetectionStrategy.OnPush?

As expected, using ChangeDetectionStrategy.OnPush on the ChildComponent and then pressing the button will not trigger any changes!

Why does this happen?

When we using OnPush strategy we’re telling Angular that the component only depends on its Inputs() and needs to be checked only if the reference changes. In our case, we add a new item to the array but the reference is kept the same.

So how can we use the advantage of OnPush strategy to optimaize the app from one hand, but still track changes in array like objects on the other hand?

@TrackChanges Decorator

This decorator will help us to track changes inside components using OnPush strategy.

Side note: in order to deal use cases like above we can —

  1. We overiting DoCheck hook in order to monitor changes that occur where ngOnChanges() won't catch them.
  2. To check if our array has a new items we will use IterableDiffers class.
  3. Finaly, in case there are changes, we will trigger changeDetection() manualy using markForCheck() function.

Now we can apply our decorator and pass the name of property to track.

Cool! as we can see above, pressing the button will trigger change detection manualy (after performing a diff using IterableDiffers class) and will bring back the smiles 😍

Side note: as of today there is no way to inject classes/services inside decorator function, so in order to use services like ngZone, IterableDiffers, ChangeDetectorRef etc.. we need to inject directly to the compenent class and then we will have a reference to the injected classes/services inside the decorator function.

Summary:

As I mentioned at the beginning of the article- the decorator is a very powerful tool. In this article you have seen several practical examples which I hope will make you curious and explore this subject further.

Software Developer

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store