Understanding Decorators in Angular
We’ve already been exposed to decorators a little bit. Specifically, we have seen @Component
and @Directive
decorators. A decorator in Angular looks like this:
@Component({
someThing: 'some-value',
someOtherThing: [Some, Other, Values]
})
export class SomeComponent {}
They definitely look a little weird - they are like little hats that sit on top of our classes - but they play an important role. Their role in an Angular application is to provide some metadata about the class you are defining, i.e. they talk about your class rather than define the class.
The concept of a decorator is not an Angular specific concept. This is a common design pattern in Object Oriented Programming. It isn’t necessary for us to discuss the general applicability of the decorator pattern, but let’s consider what this is achieving for Angular.
I think that a @Component
decorator shows this quite well:
@Component({
selector: 'my-component',
template: `<p>Hello</p>`,
styles: [
`
.some-class {
display: none;
}
`
]
})
export class MyComponent {}
If you prefer to define your templates and styles in separate files you can do this instead:
@Component({
selector: 'my-component',
templateUrl: 'my-component.component.html',
styleUrls: ['my-component.component.scss']
})
export class MyComponent {
}
We have the class itself which handles all of our logic for us, but that is not all Angular needs to know to create the component. Consider that we are able to add a component to the template of another component like this:
<my-component></my-component>
Angular needs to know what name to give that tag and that is what the selector
property in the decorator allows us to do. We also need a template associated with our class, and the decorator allows us to do that with the template
property (or the templateUrl
property if you want to link out to a separate template file rather than defining it inline in the decorator). The same thing again with the styles
property. In this case, this decorator is telling Angular that:
- This class is a component (by virtue of the fact that we are using the
@Component
decorator) - It should have a selector of
my-component
- This is the template for the component (or this is where to find it)
- This is the styles for this component (or this is where to find it)
With all of that extra information, Angular can do its job and create the component.
@Directive
We already know about the @Component
decorator, now let’s cover all of the common decorators you will be using one at a time.
The @Directive decorator allows you to create your own custom directives. We’ve touched on the concept of a directive briefly, but basically, it allows you to attach some behaviour to a particular component/element. Typically, the decorator would look something like this:
@Directive({
selector: '[my-selector]'
})
Then in your template, you could use that selector to trigger the behaviour of the directive you have created by adding it to an element:
<some-element my-selector></some-element>
We talked before about a directive that would make the background colour of an element red
. Let’s take that a step further and create a directive that will change the background colour to a random colour.
I am going to create this directive for the home page component in the example app we have been working with. If your root component no longer displays your home component, I am going to leave it to you to figure out how to get that displaying correctly again.
Create a file at
app/home/ui/random-color.directive.ts
and add the following:
import { Directive, HostBinding } from '@angular/core';
@Directive({
selector: '[randomColor]',
})
export class RandomColor {
@HostBinding('style.backgroundColor') color = `#${Math.floor(
Math.random() * 16777215
).toString(16)}`;
}
NOTE: This is a sneaky little technique to create a random colour in JavaScript. I definitely did not come up with this, I stole it from here.
I am sneaking in an Angular feature here that we haven’t talked about yet. The idea with a HostBinding
is that it binds to the host which means the element/component that the directive is attached to (remember the comparison I made about the directive being like a parasite, well that analogy holds here because a parasite attaches to a host). We can use this host binding to change some property of the host.
In order for this directive to work, we will need to declare it in our root module because that is where our home component is declared:
app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HomeComponent } from './home/home.component';
import { RandomColor } from './home/ui/random-color.directive';
import { WelcomeComponent } from './home/ui/welcome.component';
import { SettingsComponent } from './settings/settings.component';
@NgModule({
declarations: [
AppComponent,
HomeComponent,
SettingsComponent,
WelcomeComponent,
RandomColor,
],
imports: [BrowserModule, AppRoutingModule],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}