TL;DR: I built a simple example using the recently released Angular CDK. Jump to the code at the end of this post or visit the working demo in Stackblitz.
Angular Material CDK
Just as a little bit of introduction, the Angular Material team released a few days ago the Angular Component Development Kit. I won’t put much effort into explaining what the CDK is so I’ll rather quote them here:
The goal of the CDK is to give developers more tools to build awesome components for the web. This will be especially useful for projects that want to take advantage of the features of Angular Material without adopting the Material Design visual language. — Angular Team
So it’s pretty clear that the CDK is comprised of a bunch of services, directives, components, classes, modules, etc to make our lives easier when developing Angular components. In the current first release they included features for accessibility, text directionality, platform detection, and dynamic component instantiation. I’ll be talking specifically about dynamic component instantiation in this post. There’s no documentation yet but you can take a look at the code here. The CDK is released under the @angular/cdk npm package.
Why should I even consider using this?
The guys behind the CDK are the same guys behind Angular Material. One of the missions of the Angular Material team has been to lay out a pretty good foundation of how to properly build components in Angular taking into account performance, a11y, i18n, scalability, etc. So, after almost 3k issues closed, I’m sure they know a thing or two about how to build good components for the web. Using the CDK is a safe way to ensure you include the best practices into your component production workflow.
Portal and PortalHost
I’ll do my best to explain these two core concepts which are heavily used in the Angular Material library itself. There’s a brief documentation about this subject inside the CDK source code which you can visit here. Simply put, the CDK exposes an API to let you do dynamic component instantiation via Portals and Portal Hosts. It means you can easily inject Components or Templates inside any other element whether it is an
ng-template or a plain DOM element (such as
document.body) . Let’s break down the concepts of
Portal: the piece of UI that you want to inject anywhere else. It can be either a Component or TemplateRef.
PortalHost: the place where you want to render the Portal. There’s a specific implementation of the PortalHost called
DOMPortalHost which basically lets you attach a Portal to an arbitrary DOM element outside of the Angular application context, such as the
Before we jump into a example of how to use these concepts in a real world scenario, I’d recommend you to visit the
portal.ts implementation inside the CDK source here. Do not try to understand the specific implementation details (do it if you want) but instead take a look at the classes and their methods. You’ll be quick to grasp what each class and method does by just reading their names. This will give you a good feeling of what these utilities do for you.
In this example we’re going to build a simple service/component that lets us show a full width/height Loading… message. This really comes in handy when you load data from an API in several screens. The following code snippets assume the CDK has been installed via
npm install @angular/cdk .
We’ll be appending a component into the
<body> of our document so the following code will make a lot more sense if you can first take a look at the vanilla, non-cdk approach in this post.
First thing we should do is to create a component with the Loading… message. It can also have a spinner, a shadowed backdrop, fade in/out animation, etc. As this is not the core of this post, I’ll assume you already know how to do that and I’ll skip the implementation details here. The one thing that you should take care of is to add your component to your module’s
entryComponents. For the sake of the example, let’s name our component
This is where we apply the concepts learned in this post. We’ll build a service
LoadingSpinnerService that exposes two simple methods:
hide() which will reveal and hide our loading spinner, respectively (duh).
Breaking down the gist
I really feel that the code is pretty much self explanatory but I’ll add some remarks anyways. Please follow the numbers in the snippet comments:
- Create a reference to the Portal: As mentioned earlier, the portal is the abstraction used to contain a Component or TemplateRef. In this case, we’ll have a Portal built from our previously created
- Create a reference to the PortalHost: We’ll be using the
document.bodyto inject our component. We chose a DOM element as the host’s anchor so we use the
- Inject dependencies: These dependencies are needed by the
DomPortalHostto build a PortalHost. If you want to understand what the CDK does with all these dependencies, visit this post in which I go through all these specific details.
- Create Portal: There are two implementations of the
TemplatePortal. We use the former as our constructor because we want to create a Portal based on our
- Create PortalHost: Same as point 4, we use the constructor to build a PortalHost based on a DOM element, the
<body>. If you’re wondering why I chose the
document.bodyto attach the component, the reason is simple: the loading spinner is a component that should be placed on top of any other element in the screen and we don’t want to be dealing with stacking issues if placed inside any other element. Besides, it works as a dialog so, by architecture, it shouldn’t be part of any component’s descendants.
- Reveal the spinner: Show the component by simply attaching it to the PortalHost. Simple as that.
- Hide the spinner: Hide the component by simply detaching it from the PortalHost. Simple as that.
Working demo (credits to Gethin Oakes): https://stackblitz.com/edit/angular-cdk-portal-basic.
I hope this post inspires you to go and play with the new Angular CDK. The Angular Material team will for sure be adding more features to the CDK in the upcoming weeks so stay tuned.
If you liked this please show some love and share. Reach out to me at Twitter @caroso1222.