In this post I dive into some of the features included in the recently released Angular CDK. Specifically, I’ll talk about Portal and PortalHost.
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.
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.
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.
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
and PortalHost
.
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 document.body
.
A Component or Template Ref is attached to a Portal. A portal is then attached to a PortalHost. A PortalHost can be an ng-template or an arbitrary DOM element.
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 LoadingSpinnerComponent
.
This is where we apply the concepts learned in this post. We’ll build a service LoadingSpinnerService
that exposes two simple methods: reveal()
and hide()
which will reveal and hide our loading spinner, respectively (duh).
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:
LoadingSpinnerComponent
.document.body
to inject our component. We chose a DOM element as the host’s anchor so we use theDomPortalHost
implementation of PortalHost
.DomPortalHost
to 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.Portal
abstract class: ComponentPortal
and TemplatePortal
. We use the former as our constructor because we want to create a Portal based on our LoadingSpinnerComponent
.<body>
. If you’re wondering why I chose the document.body
to 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.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.