This post is based on something I learned that completely changed the way I designed Angular applications. Core UI components in the app such as buttons can change a lot, which can be a pain to maintain if you have to fix this in a lot of places. One example is using Angular Material buttons and then your UX’er wants to change the buttons to something different and now you need to find all the places in your application where the Angular Material buttons are used and fix them. Does this sound DRY to you?
The solution is to apply the adapter pattern which means to separate the abstraction and the implementation. This means that the interface can stay the same while you can change the implementation. Also, it gives the benefit that you can get to choose the interface you find useful and use that everywhere. Now, as a contrast to using “raw” Angular material buttons, you only need to change the adapter component one place to change all buttons in the application.
When should I use the adapter pattern?
You should use the adapter pattern for central UI components that may be hard to change without, eg. buttons, checkbox, and selects.
Applying the adapter pattern in an Angular app
We are going through how to apply the adapter pattern in an Angular app by wrapping the buttons into an adapter. For a complete demo of how this is done watch my demo project.
Creating the buttons adapter components
We are gonna create an adapter for buttons in our application, so they have the same interface and we can change all buttons by only change one central place.
First, clone my demo project or create a new Angular CLI project by opening the terminal and write:
ng new adapter-demo
We are gonna create a buttons adapter inside a folder called shared folder, which is where the shared components and pipes go.
We can generate a new buttons module with:
ng g m buttons
This creates a folder for buttons. We want to set up the following structure:
ButtonsModule is the module for all the buttons in our app. It exports a list of parent buttons and each is acting as a button adapter, wrapping the actual implementation of a button with an abstraction.
When looking at the folder structure picture, you see that we have button parent components and button child component. Button parent components are an abstract class and are the abstractions, that are actually used externally in the app and button child components are components internal to a button parent component.
The code for the button’s parent abstract class look like this:
This is the external interface to our button adapters. This interface allows us to specify a
buttonType (can be Primary or Secondary here), set the button as disabled and add style and classes to the button. We use the
onClick method and clicked event to support disabling the button.
To avoid parsing inputs and events around in the template for each child button component, the child components just need to inherit the
ButtonChildComponent abstract class:
Now, let’s say that we want to create an adapter for all the square buttons in our app. We can create such a square component as:
We see that this simply inherits
The template for the SquareButtonComponent looks like this:
This switch case is for supporting three buttonTypes: primary, secondary and default.
Let’s look at how to create the primary buttonType:
The component class is simply inheriting our
ButtonChildComponent for making it get inputs and send events from the parent.
Finally, the actual implementation detail of the button is in the template:
This is styling the primary button as a Bootstrap primary button and is enabling passing of classes, style, and disabled state. When clicking the button it will call onClick and trigger the parent’s click event. This way, if we wanted to change the primary button to eg. Angular Material buttons, we only need to change this component and it will reflect everywhere.
This component can now be used like this:
In this post, we saw how the adapter pattern can provide your Angular app with good interfaces and flexibility. The benefit of this is to be able to change the implementation, without affecting the interface of the adapter component. We saw how to apply this in an Angular app that used Bootstrap as the implementation of the button adapter. This made our application’s buttons easy to change by having a stable interface and only needing to change implementation details one central place.
Thanks for reading. Remember to comment, share and follow me on Twitter.