The Angular Component Dev Kit (CDK) is a library of predefined behaviors included in Angular Material, a UI component library for Angular developers. It contains reusable logic for many features that are used as common building blocks for the interfaces of Angular applications.
It’s a simple, time-saving platform to consider incorporating in your development projects where appropriate.
The Angular CDK gives developers solid, well-tested tools to add common interaction patterns with minimal effort.
Jeremy Elbourn, FE Engineer @ Google on the Angular team.
In this blog, modal refers to any content that appears on top of everything else and blocks interaction with the rest of the page e.g modal window. Modals could be used to request confirmation from the user for certain actions, display a message that needs the attention of the user, display a larger view of an image or video, or display a form.
How Angular CDK Features Spare Developers’ Sanity
Some Angular CDK features include logic for horizontal alignment of content (left to right, or right to left), ensuring that the position of a drop down menu is only within the limits of the viewport, defining keyboard accessibility, creation of data tables, wizards, overlays, and management of scroll behavior — just to name a few.
The Angular CDK simplifies adding and reusing logic for these features across digital interfaces.
This is key because writing the code for such UX features across a digital ecosystem from scratch makes for a lot of work.
Using Modals to Easily Control Data Entry
A recent client project required large amounts of data to be entered via modals for a diverse set of UI dialogs and popups, as well as confirmation dialogs for certain user actions.
Documentation about the Angular CDK features required for these modals was hard to find when the project started last year— and the situation now is not much different.
Hopefully this blog helps to change that.
By exploring and using existing feature components made available through Angular CDK, I was able to reduce the time spent writing new code and speed the development phase of the project. I used all available resources including the official repository of the Angular Material source code of the unit tests to understand how the overlays and portals work and how to leverage them to create modals.
Through my research I found three different solutions for using and integrating Angular CDK components to create manageable Angular based modals, all of which can co-exist in the same project, yet function independently.
1. Self-Closing Smart Entry Components
First, I followed a tutorial from Dominic Elm on blog.thoughtram.io. The tutorial implements a simple image viewer, but I adapted it to use a smarter component that uses the remote-control1 since I was managing a complex, dynamic, and reactive form that needed to conform with the client’s specific UX requirements.
This can be opened only from the outside by a service call and is capable of using the injected reference to close itself.
Something new for me was using the ComponentType interface from the Portal Module. This helped me accomplish the goal of having Component independent service. Now the caller component passes the component class to open in the modal as a type of the open function and as an argument.
In my caller component I just write one line:
The solution worked well for this specific situation where the application needed to open a form where the user could edit his or her profile.
This component e.g. EditProfileComponent allows us to close or submit the form. A large part of my data flow is in ngrx store, so I have properties in the state that give me greater control of UI elements — from indicating when data is being loaded from the server to disabling forms while data is transmitted to the backend.
In this case, after the data is submitted, I set a pending property to true in the store so I can then show a loading indicator. Then I disabled the form while the service call is pending. After the calls succeeded without errors, I set a success property in the store to true and pending to false.
So the component subscribes to changes in the store and I’m able to do this:
2. Remote Controlled “Dumb” Entry Components
This approach is completely different. I modified the remote-control1 class so that it has a BehaviourSubject events property that would emit possible actions to a created subscription in a caller Component.
You can see by the name that this type of modal will be used for system-generated confirmation of users’ actions.
In the caller component I have created a method that takes care of opening and subscribing to events from the reference and performing tasks according to the event.
According to purpose of the modal, we chose to use different methods to close each of them. One method is via the use of a button within the modal, the other is to use a listener on the overlay. For this purpose, the Overlay instance has an exposed listener for clicks. Here the developer could enter special logic that either calls a close instruction to the modal instance or doesn’t do anything at all.
In this specific scenario I have added a condition if close of the confirmation dialog should happen by setting additional configuration, as in the following example:
3. Remote-Controlled Template-Based Modals
There came a point in the project where one interface incorporated multiple modals with similar formatting, yet different content settings.
I needed to figure out how to leverage the ng-template for all of these modals in order to streamline the management of their complex settings that needed to support each user’s actions.
I created a new remote-control1 class to make sure I kept things separated.
I knew I should use a Portal instance rather than the ComponentPortal, because template-based modals are not components. The ComponentPortal accepts a Component and creates a Portal containing this component, while template-based modals use the cdkPortal directive to return a created Portal instance, which is all that I needed to attach to the overlay in the UI.
With that in mind, I changed the code from the modal service from this:
In addition, if I wanted to control the size, the position, and other UI features of the modal, I could do that by sending additional parameters and changing the configuration interface.
I’m using a custom configuration object in getOverlayConfig method because I need to set up the position before returning the overlay settings, which are used for the creation of the modal.
So, after creating my new service for opening and closing template-based modals, I needed to add the modal itself in my html template.
This can be done with the help of the the ng-template component and the cdkPortal directive from the Portal Module. The ng-template can hold simple html or a component.
Next, I retrieve the reference of this template.
Now the modal can open at the user’s request.
The dialogRef is declared on top the component, like so:
When I need to close the opened template modal, I will code something along the lines of the following:
But that’s just the beginning. You can check out other strategies to implement modals in your solution, like positioning a modal inside a component or enabling the scroll of the page behind the modal.
What is the Best Method for Implementing Modal Windows?
People might wonder which method for implementing modals with the Angular CDK is the best. But I think that every method has its benefits.
Smart modal components — demonstrated in the first example — are quite valuable. A modal that handles its behavior alone and doesn’t depend on external method calls to manage the closing of the modal or the saving of the data is appropriate if the Angular modal is openable from multiple places and has complex logic incorporated inside.
Almost every website with a login portal has a profile page for its users, where they can edit their avatar, name, and other credentials. Often, the designer doesn’t want to navigate users away from their current page to a specific Edit Profile screen. The solution is to open a modal with this form. This form could be called from a dropdown menu coming from an avatar in the header navigation, or from a side navigation menu, or from the profile page itself.
The second option, a remote-controlled “dumb” modal like the confirmation modal, also has its benefits. It’s declared once in the modal service directly and I don’t import it anywhere else. It fires events to the UI that prompt the user to action. Once ConfirmationModalService is included in the project I can launch a confirmation dialog from anywhere in my app.
A negative impact of the first two approaches is that the component launched with ComponentPortal has to be included in the entryComponents of a module.
This means that the component has to be included in the initial assets load, which means incorporating more logic that will actually slow down your load time. This doesn’t have a huge impact in small-scale situations, but for large applications that must support more interfaces, a solution that used to work can quickly become a headache for both the development teams and the product owner.
Since the application referenced in this blog was built with an architecture based on lazy load modules (which are not included in the initial load, but are loaded when a user navigates to a specific page) this route did not actually pose a problem in our development strategy.
Personally, I would use the third method, remote controlled template based modals, if I have multiple different modals in one component. I can put whatever I want in the ng-template, be it just html or another component. It would be easy to achieve consistent UI/UX over multiple modals. These type of Modal windows will load with the parent module that imports them, thus not affecting the web app load time globally. Also, having the template of the modal window directly in the host component template provides a flexible and easy way to pass different data attributes and event callbacks on demand. The previous examples both need to use special Injection Tokens to pass data to the Smart Modal window and do not provide a way to bind to events emitted by the component, making them less flexible. Instead the smart-entry component has to leverage event emitting via the reference, like the confirmation modal window described earlier.
As a negative I would point out that having multiple template-based modals in one component/page view requires multiple usage of ViewChild decorator. ViewChildren does not provide easy solution to differentiate between all the templates. ViewChild is not accessible in OnInit lifecycle hook, but only in AfterViewInit and this might provoke some changes in your code around when the data needed for the Modal window is initialized. Managing which modal to show when requires some logic to be written by the developer as part of the implementation.
You can find the full working code here.
1 I’m using “remote-control” and reference in the same sense. The reason is that the first came out as jargon to me from the Dominic Elm’s blog post and second to me makes more sense technically as it is actually a reference to an instance of the modal window.