Remember the first time I saw the '@' symbol in my first Angular application, what is this and why do we need it?
The combination of @ and another word forms a term called decorator.
Decorator is a special kind of declaration or attribute that can be attached to a class, method, property or parameter to change the way they behave. It is considered as a design pattern.
Before we dive into decorators and their magic influence, there are two things that worth mentioning:
In typescript, there are no built-in decorators, you must build your own.
Before we build a decorator, we must enable a compiler option, since decorators are still experimental and their implementation may change in the future:
In your tsconfig.json file uncomment the "experimentalDecorators" setting:
Decorator Factories:
To build our own decorator, we need a factory function that returns the expression to be executed by the decorator at runtime
Class Decorators:
If you are familiar with Angular look at the next example, if not don't worry you will build your decorator soon:
A component is just a class but how can Angular know it is a component not an ordinary class.
Yes the answer is decorator, Angular has many built in decorators, one of them is @Component which tells angular that "hey, I am a component!"
import { Component } from '@angular/core';
@Component({
selector: 'app-server',
templateUrl: './server.component.html',
styleUrls: ['./server.component.css']
})
export class ServerComponent {
}
As we have said, typescript has no built-in decorators but for a component to work you must import it from angular core components.
Behind the scenes a decorator is just a function that is called by JavaScript runtime.
To build or own class decorator we need a function. To inform the function that it is a class decorator, pass constructor as a parameter. Do NOT forget the type Function, we use TypeScript!.
function Component (constructor: Function) {
console.log('Hey! I am a component.');
}
@Component
class OurNewComponent {
}
Let's try this. Now we can say welcome to our new component.
We can improve our component by making a parametrized decorator:
type ComponentOptions = {
selector?: String,
templateUrl?: String
stylesUrl?: [String]
}
function Component (options: ComponentOptions) {
return (constructor: Function) => {
constructor.prototype.options = options;
}
}
@Component({
selector:'my-app',
templateUrl: 'app.html',
stylesUrl: ['app.css']
})
class OurNewComponent {
}
Looks like our angular component decorator, doesn't it?
Method Decorators:
We can add a decorator before a method to add a logic before, after it or even change the entire behavior of the function.
function Server(target: any, methodName: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value as Function;
descriptor.value = function (...args: any) {
console.log(`Server is being started.`);
originalMethod.call(this, ...args)
console.log(`Server status has been updated!`);
}
}
class MySever {
@Server
getServerStatus(status: string, port: number) {
console.log(`server is currently ${status} and running on port ${port}.`);
}
}
Before @server decorator, the function just prints a single line but after adding this decorator it has changed the behavior of the function leading to the following result:
There is something I want to mention here. As you know, we try not to use any as much as possible but in this case the compiler expects this type from us.
As we have learned about decorators, how can frameworks like angular use decorators and how we can build our own class and function ones. You must have realized how powerful decorators are and how they can be used to change code behavoir.
We covered both class and method decorators but there are still accessor, property, class decorators and how to use them with Express.js. So, if you like to decorate your code more stay tuned!
Top comments (0)