Using the Adapter Pattern in Angular Apps for Easier Maintenance

Christian Lüdemann
5 min readSep 23, 2018

--

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:

The 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 ButtonParentComponent.

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:

Integrating an adapter UI component with forms using ControlValueAccessor

If we have UI components, that are taking input’s we normally want to hook it into the form handling so we can do validations and show error messages.

Let’s consider we were creating a date picker component based on Angular Material’s date picker. We want to support error handling as well as show an error message in case of errors. To support integrating with the host component’s form control, we will need to implement ControlValueAccessor:

The date picker is implementing ControlValueAccessor by creating the methods: writeValue, registerOnTouched, registerOnChange and setDisabledState.

Let’s go through what each method does and how each of these is implemented.

writeValue

This method is triggered when a value is set on the component either through [ngModel] (template-driven forms) or formControl.setValue(someVal) (reactive forms).

When this is triggered, we set the date on the date property.

registerOnTouched

This method is for setting a callback function for marking the control as touched. We save the callback method in a property and call it in the onDateChange method.

registerOnChange

Like for registerOnTouched, but for triggering value change events. This method is for setting a callback function for triggering a value change event. We save the callback method in a property and call it in the onDateChange method with the newly changed date.

setDisabledState

This method will be called when the component is set as disabled using [disabled]="true" in the host component. We can then pass that value to a isDisabled property in our component and use it to disable the internal date picker implementation.

Conclusion

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.

Originally published at Christian Lüdemann.

--

--