Harnessing the power of Mixins in Angular

Armen Vardanyan
JavaScript in Plain English
5 min readAug 29, 2019

--

What is a mixin? As per version 2.2, TypeScript now supports the concept of a mixin — a function that can take a class, extend it with some functionality, and then return the new class, allowing other classes to extend from it — allowing classes to mix and share functionalities!

How it works?

The concept is fairly uncomplicated — if we are familiar with inheritance, higher-order classes/functions and their syntax, we can jump right into them. Here is the example from the TypeScript documentation itself:

As we see, we use a function here to create an enriched version of another class, which can be used both to instantiate new objects and to extend other classes. In a sense, this now allows for multiple inheritance — if some of our classes are only needed to share functionality (abstract class), then we can write it inside a function, so it can be mixed with other classes for further composition.

How can this be used?

Imagine a usual Angular application with different pages, some of which have forms in them. All is well, then one day we decide, that from now on, if the user touches a form, but tries to leave the page without saving it, a window should be displayed, asking the user to confirm if they really want to leave the page (a very standard feature).

Of course, the first thing that comes to mind is a Guard. Because our app is designed in a good fashion, most of the (but not all!) components with forms in them have a generic form field, which is an instance of AbstractFormControl. Of course, from then we can check the touched field of the form property and find out, if it is necessary to show a prompt. The guard can look like this:

Of course, this looks good on paper, but in reality, as mentioned briefly above, not every single page works like this — some pages have multiple forms, some pages have forms inside their child components, some have both. Of course, we want the guard to work in the same way for each component, and we also want to be very precise and consistent. So here is a thought: from now on, every component that should have this guard on it, should implement a special method called isFormTouched, which will return a boolean to tell the guard if it has to show the prompt or not. Here is an example of a more complex component, which has a form not inside itself, but rather inside its child component, and how it implements the method:

Of course, this is great, but for most components (like, 90%) the isFormTouched method comes down to just this:

Of course, we can copy and paste this method to each and every component in which we need it, but that action itself smells fishy, right? A reason why we don’t want this solution is because one day, it may be also required to check if the form has already been submitted (using an isSubmitted property on the same class). That would require us to rewrite lots of code. And if we miss one instance - it may be ages before someone finds out such a minor bug. So, naturally, we want a solution which lets us write that particular method just once, but still share it among all our components that need it. Naturally, inheritance comes to mind - but here are three fundamental downsides to it:

  1. Idiomatic: inheritance is meant to represent an is-a relationship (as in “a horse is an animal”), rather than share functionality. Dependency injection or object composition are used for that.
  2. Paradigmatic: what should this class even represent? In some OOP languages a concept called a trait is present, which allows such things, but in JS we don't have such a feature, so our classes should represent something, not just contain one simple method.
  3. Practical: what if in the future we need to extend our component from another base class? Our hands would be tied in that case.

Mixins to the rescue

Take a look at this function:

As you see, this function takes a simple class (which can also be an Angular component) and returns another component, which extends from it and also has the isFormTouched method implemented on it. We can then do this simple thing:

Here is how this functions solves all of the three issues mentioned above:

  1. Because it is named WithFormTouchedCheck, it now represents a class (which it can get as an argument - or without it, notice the Base: Constructor<T> = (class {} as any) line, which basically means that if no class is given, en empty one will be extended) that was enhanced with some additional features.
  2. This function is not a class in itself and only represents a functionality that it adds to an existing one — granted it implements a certain interface — that is why it is declared as abstract).
  3. Because it accepts another class as an argument, it now allows us to extend our component from other classes too. As a matter of fact, as long as we keep this format, we can have as many classes as we want!

Mixing thing up

I am sure you many of us have heard the scary stories about zombie subscriptions, and how we can avoid them by using the takeUntil operator. Of course, we would also need to create a specific Subject for that, and send a next notifications inside our ngOnDestroy method. In fact, here is the code:

Lots of our components may have subscriptions inside them, and all of them should implement this same functionality, so it makes sense to convert it to a mixin:

Of course, a component that uses our previous mixin may also need this new one, but it is extremely easy to combine them:

Be careful: we implemented the ngOnDestroy method in our mixin: if you are going to use it in a class, which will also itself implement the ngOnDestroy method, be sure to call super.ngOnDestroy() inside it!

Cons

Of course, every approach in programming may have some downsides to it. Most of them are in our case are related either to some Typescript compiler specific issues, or to Angular build issues. Here I present two of them, which may be the most frustrating ones.

1. Decorators are not valid here issue:

If we try to use a decorator inside our mixin like this:

We will get an error which says Decorators are not valid here. This is in fact an issue with Typescript compiler. Here is a workaround:

Somehow Typescript is only okay when we put decorators on a named class

2. Angular Inputs issue:

If we include a decorator on one of our mixin classes to, for example, define an Input property on the class, it will work as intended when we ng serve our application, but will throw an error during a production build. This is related to this issue. A workaround for this is a bit messier:

This is of course more work to do on a child component, and Angular’s own style guide does not approve the usage of the inputs array in the decorators, but this is still a workaround. I personally don’t mind it, until the Angular team provides us with a solution.

Conclusion

Typescript mixins are a great way to enhance our Angular app with shared functionality without breaking any flow. While this technology still has some small downsides, I personally believe its benefits vastly outweigh them.

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

--

--