A short introduction of attribute directives
Angular attribute directives essentially allow us to add behaviour or change the appearance of existing elements or components of our own. They enable the reuse of behaviour, style or logic. Hence, keeping us DRY (Don’t Repeat Yourself). Lots of people myself included didn’t know the immense power they provide. So you might have fallen into the trap of repeating some code in various components not realising you could abstract it into an attribute directive. Essentially Angular attribute directives can be seen as an application of the decorator pattern, which you may already be familiar with.
Angular has three types of directives. Component Directives (the ones you’re most familiar with through @Component
and have their own template), Structural Diretives(You use them a lot like *ngFor
, *ngIf
or *ngSwitch
, and finally Attribute Directives which are the focus of this article.
Attribute directives are defined using @Directive
and can be applied as attributes to HTML elements.
They don’t have their own templates like components their sole purpose is to modify behaviour or appearance. They act as modifiers rather than structural UI components.
This article will show you how to build your own attribute directive and provide you with best practices and pitfalls to look out for.
Building your own attribute directive
Say for instance you’d like to be able to apply an attribute that enables character counting for any input or textarea field. You want to reuse this behaviour across your applications. In the code playground below we will implement such attribute directive.
So if you play with this you notice that both textarea and input field have their number of characters entered displayed below. So now we can put this anywhere where an input field is present. We’ve kept ourselves DRY. No longer repeating that same code. Great win for reusability. We also keep components and templates clean as well by not having to implement this. Hence achieve single responsibility principle.
Now it’d be great if we could do more with this attribute. Maybe get a call when character limit is reached and a limit is present. So now that would take us into a territory where we need to provide inputs and outputs to our directive. Lets dive in.
We will use Angular’s new signal inputs to improve readability. we need a new input max
and an output called limitReached
. Let’s do it:
Try it on the textarea. I haven’t added it to the input below. You’ll now notice now that when the max characters is reached you’ll receive a pop up.
<textarea appCharacterCount [max]="20" (limitReached)="onCharLimitReached()"></textarea>
If we didn’t provide max
this would have not happened.
Best Practices
Here are some best practices to follow:
- Single Responsibility Principle (SRP): Each directive should have a single well-defined purpose that aligns with decorator pattern
- Don’t Repeat Yourself (DRY): abstract away common UI behaviours into reusable attribute directives instead of duplicating logic across multiple components
- Clear API with inputs/outputs
- use prefix for custom selectors such as
appHighlight
to avoid naming conflicts - group shared directives in a module
Pitfalls to look out for
- Avoid over engineering of minor behavioural change. Sometimes, a simple class binding or style binding is enough. Avoid creating directives that unnecessarily complicate the code base or introduce more layers than required.
- Avoid boolean inputs if the directive’s behaviour should be conditional consider using structural directives such as (
*ngIf
) - Avoid tightly coupled logic in side of directives that depend on specific components or services, making them less resuable. Directives should encapsulate generic behaviours that can be applied broadly.
- Move complex logic out of templates into directives typeScript class or dedicated service
Summary
This short article only scratches the surface of what’s possible with Angular attribute directives. I encourage you to head to the Angular docs and experiment with some of their other features. Overall, attribute directives have these benefits:
- Foster code reusability and modularity
- Can make your code more maintainable
- Improve the developer experience by making the codebase intuitive and reducing the cognitive load required to comprehend UI behaviour
- Allow us to add supplementary logic to elements without altering their inherent structural composition
That’s all see you next time.