Directives vs Components

Or why I prefer Angular to React

Recently I was browsing Twitter, when I came by this meme:

This made me think, how often do component abstractions mislead us.

Of course abstraction is generally a good thing. We abstract away implementation details to deal with the results of some computations without feeling they are too complex; we abstract away complex ideas to have a smarter interface of interacting with the problems we aim to solve.

But sometimes abstraction can bring on unexpected problems, because not knowing the internals means we put lots of trust in people behind the open source code we use in our projects. This is not inherently bad, of course, we won’t have the modern web as we have it today without open source, but the problem still stands — if someone made a mistake inside an abstraction, it would be very hard for the users to fix that. In most cases, the users would have to adapt, or use ugly workarounds.

So naturally, we want the abstraction to be as thin as needed, not to take too much control from the end user, and simultaneously not to expose the implementation details.

Using the Occam’s Razor, we are going to understand when and what to abstract away.

The component abstraction

But what does it have to do with directives, components, React and Angular, one may ask? Here’s what:

In the abovementioned meme, the component that was expected to render a button, maybe with some preapplied styles, in fact rendered some unexpected structures with div-s and etc. In other words, it exposed only the information about the button being rendered, and covered the information on other things that go on and affect the application.

This is where comes the difference between components and directives.

Disclaimer: This is an opinion article, and I do not intend to claim that Angular is better than React, or vice versa. In fact, I greatly enjoy both. This is more about the paradigm and the thinking of building a UI, rather than specifics of individual frameworks.

So what is a component, and why it might not be a good idea to wrap native HTML elements inside them?

A component is a piece of code that represents a custom HTML element, a reusable container for some logic and DOM. While greatly popularized by React, it is now part of almost every frontend framework out there.

The main point of a component is actually incapsulation and abstraction. We need some logic and view bound together, so here comes the incapsulation. We need to reuse a piece of UI without worrying about the implementation details — here comes the abstraction.

But it is actually the incapsulation that sometimes makes the abstraction behave clumsily. Consider the button we already saw above. The code for it may look somewhat like this:

So, here is one problem. The user expects a button, but gets a div with a button. And the styles are actually applied to the div, rather than the button.

But there is more to it. Consider this: the creator of the button makes it perform something on click, for example. This would probably look like this:

As you see, I added a handler for the click event on the button, which will perform something specific, again, abstracted away. Of course, if we use this button, then what is done on click is something desired, or expected, so it is not the problem itself. The problem is that now the control over the click event is taken from us. If you pay attention, you will see that I also passed down all the props to the native button element. So, suppose that when I use this component, I want to add a custom click handler. Here’s what I do.

Of course, this is something very familiar for anyone who has ever worked with React. We pass down a callback, it is then passed to the native button element. Want a fun fact about this ourCustomHandler ? It does not work. Because the Button component had already provided a click event handle down there, and we can’t have two using the onClick property. What can we do? If course we could use nativeButton.addEventListener , but we do not have access to the native button element, because it is abstracted away!

Directives to the rescue

Well, now let’s try to implement it the Angular way. And yes, you guessed correctly: we are not going to write a component. Instead, we will create a simple directive.

So this is basically almost the same thing that was implemented in React. But the difference is that instead of rendering a button and applying some custom logic to it, it takes an already existing button and adds some custom logic to it. Here is how this will be used somewhere else:

Here is what’s going on:

No incapsulation: in this case we don’t need a custom component, because there is nothing complex going on with it — we just want a simple button with some preapplied stuff (styles, event handlers, you name it) to it. The abstraction is still there: while we have some specific things bound to the button, we don’t see the internals of the directive, we just drop it on a button whenever we want to and it becomes what we want. Native: the familiar button element is still there, not abstracted away, we can do whatever we want with it because it is just that: a regular button.

The big question stands: if have custom elements, why not have custom attributes?

Because it is what a directive essentially is: a custom attribute for HTML elements.

This is what I appreciate about Angular Material: their API is mostly based on directives. Here is an example of a Material Table from the official docs:

As you see, this is mostly just an HTML table, simply enriched with directives, which add material styles and some internal layout, but at the same time give us access to the basic HTML elements like table , th , tr , td and so on.

To be completely honest

There are ways for Angular directives to be not very explicit about what they do with an element and change its internal structure. Here is an example of a “dirty” directive:

As you see, here we change the innerHTML property of our button, thus making a somewhat surprising change to the DOM. This is to say that Angular did not necessarily protect us from stuff like this.

And to be really-really honest we should mention that is not always a bad thing. It just should be blatantly obvious for the user, as it is with the Angular Material’s table, that some major changes to the view structure will be introduced with this directive (the use of the ng-container tags in that example is a dead giveaway), and otherwise the changes should be minimal. And yet again, even if there are major changes to the structure, the native elements are still accessible to us and can be manipulated to undo what we want to undo (though this is an antipattern and should be used only when no other option is available).

What about React?

So, can we get some sort of this functionality in React?

Well, most probably yes, using a function and the special React ref property. Here is an example:

We could also to this using the children property. Here is how:

Of course now we have access to the native button element, but this whole structure looks very clumsy. And, in other news, applying some styles to the props.children will be very hard. So, while giving back some control to the user of the component, we revoked some control from the creator of the component.

Angular directives allow to balance this control.

Bottom Line

I always tell people that I love Angular, but prefer JSX to the template syntax. Don’t get me wrong — I like the Angular template syntax, it’s just that JSX feels way more down to vanilla JS and Angular template syntax is just lots of abstraction. While this stands, for me personally the availability of a concept like the Directives is a game-changer. As I mentioned, it allows balance between the creator of the reusable component and its user.

And this is why Angular will always be my #1 choice.

Follow me on Medium and Twitter for more on Angular, Rxjs, React, and Javascript in general.