Uploaded by Čedomir Đošić

Angular directive composition. Let’s take a look at one of the most… by Kevin Kreuzer Medium

advertisement
02/07/2023, 15:43
Angular directive composition. Let’s take a look at one of the most… | by Kevin Kreuzer | Medium
You have 2 free member-only stories left this month. Upgrade for unlimited access.
Member-only story
Angular directive composition
Let’s take a look at one of the most awaited Angular features.
Kevin Kreuzer · Follow
7 min read · Nov 2, 2022
Listen
Share
More
Angular 15 is on the horizon, and with it, many great features. One I am incredibly
excited about is directive composition.
And I am not the only one. The directive composition has been one of the most
upvoted Angular issues on GitHub. Let's see what it's all about.
https://kevinkreuzer.medium.com/angular-directive-composition-e7d9f789323
1/18
02/07/2023, 15:43
Angular directive composition. Let’s take a look at one of the most… | by Kevin Kreuzer | Medium
To explain directive composition, we will look at an actual use case in the form of a
digital pinboard. We want to implement a pinboard that displays pins. Each pin
should display an info text as a tooltip on hover. Furthermore, each pin can be
dragged and dropped and initially rotated.
Digital pinboard with a couple of displayed pins
The code for such an application might look something like this.
<pinboard>
<pin image="rocket"></pin>
<pin image="beer"></pin>
<pin image="keyboard"></pin>
<pin image="testing"></pin>
<pin image="coffee"></pin>
</pinboard>
We have a
Pinboard
component and project a bunch of pins to it. At this point, our
pins will be displayed as illustrated on the graphic on top. This means they aren't yet
initially rotated, nor are they draggable, nor will we display a tooltip. All features
mentioned on top are missing.
Of course, we could go ahead and implement those features right in the pin
component. But luckily, our code base already contains some handy directives with
the desired functionality.
https://kevinkreuzer.medium.com/angular-directive-composition-e7d9f789323
2/18
02/07/2023, 15:43
There's a
Angular directive composition. Let’s take a look at one of the most… | by Kevin Kreuzer | Medium
DragableDirective ,
a
RotateDirective
and a
TooltipDirective
at our
disposal. Let's use those attribute directives to add the missing features to our pins.
<pinboard #dragZone>
<pin rotate="45deg" tooltip="Ship new products" dragable
[dragzone]="pinboard"
image="rocket"
></pin>
<pin rotate="-20deg" tooltip="A good beer after a day of coding"
dragable [dragzone]="pinboard"
image="beer"
></pin>
<pin rotate="0deg" tooltip="My favourite Keyboard, the Moonlander"
dragable [dragzone]="pinboard"
image="keyboard"
></pin>
<pin rotate="10deg" tooltip="Write tests for better code"
dragable [dragzone]="pinboard"
image="testing"
></pin>
<pin rotate="25deg" tooltip="No coffee no code" dragable
[dragzone]="pinboard"
image="coffee"
></pin>
</pinboard>
Each
pin
now applies the
rotate
attribute directive and passes the specified initial
rotation degrees. Then there's the
and last but not least, the
dragable
tooltip
attribute directive with the tooltip text,
attribute with an addtional
dragZone
input.
The drag zone is necessary because you only want to be able to drag the pins inside the
board.
Nice. This is a good approach, but it has some downsides.
To make the
PinComponent
feature complete; the developer has to remember which
directives are needed and has to apply all directives by himself.
Wouldn't it be cool if we could provide the
PinComponent
with drag, tooltip, and
rotate features right out of the box and still reuse our directives?
Why do we need directive composition?
https://kevinkreuzer.medium.com/angular-directive-composition-e7d9f789323
3/18
02/07/2023, 15:43
Angular directive composition. Let’s take a look at one of the most… | by Kevin Kreuzer | Medium
So far, we could reuse our directives in our components by using inheritance. To get
the draggable functionality, for example, we could extend our
PinComponent .
export class PinComponent extends DragableDirective implements
OnInit {
}
The nice thing is that by using inheritance, we can inherit all Angular features like
HostBinding
or
HostListeners
Open in app
etc…. And it also works very well with template type
checking and minifiers.
But this approach has its limitations. What about the tooltip and the rotate
functionality? We can only extend one class, right?
Furthermore, we have no way of narrowing down the public API of the
PinComponent . The
public API of directives leaks into derived classes.
This is not an optimal solution; that’s why we now get directive composition.
If you’re interested in learning Angular or improving
your coding skills, my Twitch channel is the perfect
place for you! With live streams and interactive Q&A
sessions, you’ll have the opportunity to chat, learn
and get real-time feedback on your own projects.
Directive composition
The new directive composition API introduces a
hostDirectives
property on the
Components and Directives decorator.
The property value is an array of configuration objects. Each config objects
contains a required
directive
attribute and two optional properties
input
and
output .
hostDirectives?: (Type<unknown> | {
directive: Type<unknown>;
inputs?: string[];
https://kevinkreuzer.medium.com/angular-directive-composition-e7d9f789323
4/18
02/07/2023, 15:43
Angular directive composition. Let’s take a look at one of the most… | by Kevin Kreuzer | Medium
outputs?: string[];
})[];
Let's go ahead and try to use this brand-new property in our
PinComponent
to add the
tooltip, rotate, and drag features.
@Component({
selector: 'pin',
template: `
<img [src]="'assets/' + image + '.svg'"/>
`,
hostDirectives: [
{directive: TooltipDirective},
{directive: DragableDirective},
{directive: RotateDirective},
]
})
export class PinComponent implements OnInit {
With this, we can also remove the draggable attribute directive from the pins in our
HTML.
<pin rotate="25deg" tooltip="No coffee no code"
[dragzone]="pinboard"
image="coffee" (pinGrabbed)="pinGrabbed()"
></pin>
If we would not need to pass a
tooltip
and
rotate
those attributes since they are now provided by
PinComponent .
input, we could also remove
hostDirectives
But we still need those attributes as well as the
on the
dragzone
attribute
because those attributes are inputs.
Let's go ahead and run our app. Instead of excellent features, we get a bunch of
compilation errors:
ERROR
src/app/pin.component.ts:20:17 - error NG2014: Host directive
TooltipDirective must be standalone 20
{directive:
TooltipDirective},
~~~~~~~~~~~~~~~~
https://kevinkreuzer.medium.com/angular-directive-composition-e7d9f789323
5/18
02/07/2023, 15:43
Angular directive composition. Let’s take a look at one of the most… | by Kevin Kreuzer | Medium
Well, that's a friendly error message which informs us about one of
hostDirectives
limitations.
hostDirectives
can only be used with standalone directives. No problem. Let's go
ahead and convert our directives to standalone directives.
Standalone, what is this? Standalone components were introduced as a Developer preview
in Angular 14. If you want to learn more about it check out my article on standalone
components.
Angular standalone components
How to generate, bootstrap, and lazy load Angular standalone
components.
kevinkreuzer.medium.com
To convert our directives to standalone directives, we have to add the
property with a value of
declarations
true
array to the
standalone
in the directives decorator and move them from the
imports
array in the
AppModule .
Great, let's try it out!
https://kevinkreuzer.medium.com/angular-directive-composition-e7d9f789323
6/18
02/07/2023, 15:43
Angular directive composition. Let’s take a look at one of the most… | by Kevin Kreuzer | Medium
PinComponent's hover state with broken tooltip. The tooltip directive gets executed, but the tooltip text is
undefined.
The tooltip is broken on hover, the icon is not rotated, and the pin is not draggable.
Why is that? It seems like the directives input doesn't work anymore. But why? we
still pass them in the HTML as attributes on the pin component!
Whenever you use
hostDirectives
all
Inputs
and
Outputs
We explicitly have to provide the public API on the
inputs
are hidden by default.
and
outputs
config
objects.
hostDirectives: [
{directive: TooltipDirective, inputs: ['tooltip']},
{directive: DragableDirective, inputs: ['dragzone']},
https://kevinkreuzer.medium.com/angular-directive-composition-e7d9f789323
7/18
02/07/2023, 15:43
]
Angular directive composition. Let’s take a look at one of the most… | by Kevin Kreuzer | Medium
{directive: RotateDirective, inputs: ['rotate']}
This is an excellent feature since it gives us complete control over the public API of
our component. Let's run our code.
Rotated and Hovered PinComponent displays a Tooltip text and can be rearranged via drag & drop.
Nice, we get the tooltip on hover, we get the rotation, and of course, the Pins are
draggable. All features seem to work. What about the
In the same way we configured our
DragableDirective
Inputs
outputs
property?
we can also configure our
Outputs .
Our
, for example, emits an event that notifies you once you grab a
Pin. We can use the
outputs
property to include the
pinGrabbed
event in our public
API.
https://kevinkreuzer.medium.com/angular-directive-composition-e7d9f789323
8/18
02/07/2023, 15:43
Angular directive composition. Let’s take a look at one of the most… | by Kevin Kreuzer | Medium
hostDirectives: [
{directive: TooltipDirective, inputs: ['tooltip']},
{directive: DragableDirective, inputs: ['dragzone'], outputs:
['pinGrabbed']},
{directive: RotateDirective, inputs: ['rotate']}
]
Pretty exciting, right? But that's not all; there's even more.
Aliases
Another neat feature of directive composition is aliasing
dragZone
inputs
and
outputs .
is a pretty generic name for our Input. In the context of pins, it would be
more accurate to name the Input
pinBoard
Let's use the alias syntax to rename the
instead of
dragzone
dragzone .
property on the
DragableDirective .
hostDirectives: [
// ...
{directive: DragableDirective, inputs: ['dragzone: pinBoard'],
outputs: ['pinGrabbed']},
// ...
]
Great. Once aliased, we can use the
pinBoard
input on the pin.
<pin rotate="0deg" tooltip="My favourite Keyboard, the Moonlander"
[pinBoard]="pinboard"
image="keyboard" (pinGrabbed)="pinGrabbed()"
></pin>
The aliasing works precisely the same for
outputs .
Summary
Directive composition is a unique and exciting feature that offers the following
benefits.
We can apply as many directives to the host as we want. There are no
limitations.
https://kevinkreuzer.medium.com/angular-directive-composition-e7d9f789323
9/18
02/07/2023, 15:43
Angular directive composition. Let’s take a look at one of the most… | by Kevin Kreuzer | Medium
By default, all the Inputs and Outputs are hidden. We can use the
outputs
inputs
and
properties to include in our public API and make them visible.
Directive composition works with template type checking.
All directive features, such as
HostBinding ,
Injection Tokens, etc... work with
directive composition.
Host directives can be chained. You can have Host directives that are built on
other host directives.
As great as it is, it also has some limitations:
As discovered throughout the post, host directives have to be standalone.
Only one directive can match a component. Make sure to use a directive only
once inside a chain.
Components can not be used as host directives.
Welcome to the world of Angular excellence — angularexperts.ch
Do you find the information in this article useful? — We are providing tailored
expert support for developing your Angular applications. Explore our wide range
offers on angularexperts.ch
Angular
Angular 15
Directives
Frontend Development
https://kevinkreuzer.medium.com/angular-directive-composition-e7d9f789323
Programming
10/18
02/07/2023, 15:43
Angular directive composition. Let’s take a look at one of the most… | by Kevin Kreuzer | Medium
Follow
Written by Kevin Kreuzer
2.4K Followers
Passionate freelance frontend engineer.
❤️ Always eager to learn, share and expand knowledge.
More from Kevin Kreuzer
Kevin Kreuzer in Angular In Depth
Debug Angular apps in production without revealing source maps
Alternate approaches to handle source maps
· 12 min read · Dec 18, 2018
3K
8
https://kevinkreuzer.medium.com/angular-directive-composition-e7d9f789323
11/18
02/07/2023, 15:43
Angular directive composition. Let’s take a look at one of the most… | by Kevin Kreuzer | Medium
Kevin Kreuzer
::ng-deep alternatives
Explore alternate approaches to the deprecated::ng-deep Angular API to avoid legacy code
and hard to debug side effects
· 4 min read · May 25, 2022
154
2
Kevin Kreuzer
https://kevinkreuzer.medium.com/angular-directive-composition-e7d9f789323
12/18
02/07/2023, 15:43
Angular directive composition. Let’s take a look at one of the most… | by Kevin Kreuzer | Medium
Publishing a beta or alpha version to NPM
Take advantage of NPM tags
· 3 min read · Dec 23, 2017
1.4K
4
Kevin Kreuzer in Angular In Depth
Retry failed HTTP requests in Angular
Immediate retry, delayed retry and retry with backoff
· 8 min read · Jul 3, 2019
2.5K
9
See all from Kevin Kreuzer
Recommended from Medium
https://kevinkreuzer.medium.com/angular-directive-composition-e7d9f789323
13/18
02/07/2023, 15:43
Angular directive composition. Let’s take a look at one of the most… | by Kevin Kreuzer | Medium
Alberto García Ibáñez
Angular tricks to become a pro
Below you can find some useful tools and tricks to boost your Angular applications! The
second part of this post is here:
· 3 min read · Jan 13
341
8
Sam Redmond
https://kevinkreuzer.medium.com/angular-directive-composition-e7d9f789323
14/18
02/07/2023, 15:43
Angular directive composition. Let’s take a look at one of the most… | by Kevin Kreuzer | Medium
Standalone Components: Simplifying Angular
Let’s explore how standalone components can make our Angular apps much easier to build
and maintain.
· 4 min read · Mar 7
31
Lists
General Coding Knowledge
20 stories · 37 saves
It's never too late or early to start something
10 stories · 13 saves
Coding & Development
11 stories · 23 saves
Stories to Help You Grow as a Software Developer
19 stories · 153 saves
Alberto García Ibáñez
Angular tricks to become a pro. RxJS operators
In this post, I have gathered the RxJS tricks that I use the most to boost my Angular
applications.
https://kevinkreuzer.medium.com/angular-directive-composition-e7d9f789323
15/18
02/07/2023, 15:43
Angular directive composition. Let’s take a look at one of the most… | by Kevin Kreuzer | Medium
· 4 min read · Jan 24
334
3
Aphinya Dechalert in Better Programming
Should You Use Angular in 2023?
Where does the framework stand now?
· 8 min read · Apr 4
900
21
https://kevinkreuzer.medium.com/angular-directive-composition-e7d9f789323
16/18
02/07/2023, 15:43
Angular directive composition. Let’s take a look at one of the most… | by Kevin Kreuzer | Medium
Kagklis Vasileios in JavaScript in Plain English
10 Things You Should Know About Zone.js in Angular
What is Zone.js? How Angular uses it? This Q&A guide will answer these questions and more.
· 5 min read · Jan 17
179
5
lavanya k in JavaScript in Plain English
How to Upgrade From Angular 14 to Angular 15
https://kevinkreuzer.medium.com/angular-directive-composition-e7d9f789323
17/18
02/07/2023, 15:43
Angular directive composition. Let’s take a look at one of the most… | by Kevin Kreuzer | Medium
Now, let’s see a tutorial of angular 14 to angular 15 upgrade. This article will give you a simple
example of angular upgrade version 15. I…
· 2 min read · Feb 10
100
1
See more recommendations
https://kevinkreuzer.medium.com/angular-directive-composition-e7d9f789323
18/18
Download