paint-brush
How to Use the Angular CDK Viewport Ruler for Responsive Appsby@briantreese

How to Use the Angular CDK Viewport Ruler for Responsive Apps

by Brian Treese6mFebruary 20th, 2024
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Learn responsive app behavior effortlessly in Angular with the CDK Viewport Ruler—never miss a viewport resize event again!
featured image - How to Use the Angular CDK Viewport Ruler for Responsive Apps
Brian Treese HackerNoon profile picture
0-item
1-item
2-item

Sometimes, in an Angular app, we need to trigger something when the viewport gets resized. Like in the demo app for this post, when we open a menu and then resize it, we want to close the menu automatically. How would you do this? Well, maybe you have some ideas, but with the Angular CDK, this is pretty easy. We can use the Viewport Ruler. It’s a utility that deals with, you guessed it, the viewport. In this post, I’m going to show you how to use it. Alright, let’s get to it!


Ok, so we probably already know the Angular CDK provides many useful utilities. And the more we know about them, the easier our angular development goes. The Viewport Ruler is another example of this.


It may not be something you use every day, but it can definitely help out in certain situations. For example, in the demo site for this post, the history of the Vans shoe brand, we have a hamburger menu button, and when we click it, the main menu opens. And then, when we resize the browser, it remains open.


But let’s pretend our client wants us to hide this menu when the viewport is resized. Let’s take a closer look at how this menu is toggled.


Within our app component, we have our page content component, which contains everything we see on the page, including the menu button. When the menu button is clicked, we emit that event.

page-content.component.html

<button (click)="menuClick.emit()">
    ...
</button>


When that event is emitted, we toggle the value of our menu visible property.

main.ts

<app-page-content (menuClick)="menuVisible = !menuVisible"></app-page-content>


When that property is true, we bind a class on our menu component, which contains the style for the visible state.


<app-page-menu [class.open]="menuVisible"></app-page-menu>


So, it’s pretty straightforward, but it only toggles on button clicks right now. This is where the viewport ruler comes in.

Setting up the CDK ViewportRuler

To use it, we first need to have the CDK installed.


npm i @angular/cdk


Then, we need to have the Viewport Ruler imported.


import { ViewportRuler } from '@angular/cdk/scrolling';


And now we can use it. We start by injecting it into the constructor.


constructor(private viewportRuler: ViewportRuler) {}


Ok, now we can listen for a change with the change event observable.

Subscribing to the Angular CDK ViewportRuler Change Event

We can wire this up right here in our constructor. We start by calling the change method on the Viewpor Ruler. And we can throttle our response to this change event so that it doesn’t fire too often when resizing. We'll go with three hundred milliseconds. This function returns an observable, so we need to destroy it when needed to prevent memory leaks. To do this, we need to add pipe and then takeUntilDestroyed.


constructor(private viewportRuler: ViewportRuler) {
    this.viewportRuler
        .change(300)
        .pipe(takeUntilDestroyed())
}


Ok, now we can subscribe to the event and properly toggle our menu visible property. Now, under the hood, the Viewport Ruler uses a window resize event that fires outside of angular’s change detection. In this case, since we need our variable value change to be reflected in the template, we need to make sure to run within ngZone. So, we need to inject in ngZone in the constructor. And when this event fires, we just need to make sure that our property is set to false.


constructor(
    private viewportRuler: ViewportRuler,
    private ngZone: NgZone)) {
    this.viewportRuler
        .change(300)
        .pipe(takeUntilDestroyed())
        .subscribe(() => {
            this.ngZone.run(() => {
                this.menuVisible = false;
            });
        });
}


Ok, so now when we open the menu and then resize the viewport, it properly closes.

Cool, so that was easy, right?

The Viewport Ruler actually provides more information too. In our case, we don’t need anything else, but there’s more available if needed.

Using the CDK ViewportRuler to Get the Viewport Rect Object

To start, we can get the viewport rect which provides an object containing the bottom, height, left, right, top, and width values of the viewport.


Let’s go ahead and log this out. It’s accessed using the getViewportRect() method.


constructor(
    private viewportRuler: ViewportRuler,
    private ngZone: NgZone)) {
    this.viewportRuler
        .change(300)
        .pipe(takeUntilDestroyed())
        .subscribe(() => {
            this.ngZone.run(() => {
                ...
                console.log('Viewport Rect:', this.viewportRuler.getViewportRect());
            });
        });
}


Now, when we resize, we can see what this rect object looks like.

Using the Angular CDK ViewportRuler to Get the Current Viewport Scroll Position

We can also get the current viewport scroll position. Let’s log this out this time. We do this with the getViewportScrollPosition() method.


constructor(
    private viewportRuler: ViewportRuler,
    private ngZone: NgZone)) {
    this.viewportRuler
        .change(300)
        .pipe(takeUntilDestroyed())
        .subscribe(() => {
            this.ngZone.run(() => {
                ...
                console.log('Viewport Scroll:', this.viewportRuler.getViewportScrollPosition());
            });
        });
}


Now, let’s scroll down a little bit and resize, we can see this object consists of both the value we’re scrolled vertically as well as the value we’re scrolled horizontally.

Using the Angular CDK ViewportRuler to Get the Viewport Size

We can also just get the viewport size if that’s all that’s needed. We do this with the getViewportSize() method.


constructor(
    private viewportRuler: ViewportRuler,
    private ngZone: NgZone)) {
    this.viewportRuler
        .change(300)
        .pipe(takeUntilDestroyed())
        .subscribe(() => {
            this.ngZone.run(() => {
                ...
                console.log('Viewport Size:', this.viewportRuler.getViewportSize());
            });
        });
}


There, when we resize now, we get an object with the width and height of the viewport.

So, in this example, we were easily able to react to viewport size changes and get all sorts of information about the viewport itself with the CDK Viewport Ruler.


I hope you find this useful as you build things in Angular.

Want to See It in Action?

Check out the demo code and examples of these techniques, the stack blitz example below. If you have any questions or thoughts, don’t hesitate to leave a comment.


Also published here.