Photo by Ronan Furuta on Unsplash

Angular CDK's BreakpointObserver

Posted on August 07, 2019

Written by Preston Lamb

angular layout

(tldr;)[#tldr]

Sometimes we need to know the width of the browser window in our component’s class file. You could have a @HostListener in a component for the window’s resize event, but you’d have to maintain the service, write tests to make sure it was working properly, handle observables correctly so there aren’t memory leaks, etc. The Angular CDK’s LayoutModule has a BreakpointObserver class that you can inject into a component, provide a breakpoint (or breakpoints) that you want to watch for, and it will emit each time you cross the threshold of a breakpoint (i.e. if I want to know when the screen is smaller than 700px, it will emit a value when I cross under 700px, and then again when I cross back over 700px).

The Problems This Solves

First off, I’ve been using Angular daily for almost 4 years now, since before the first Angular 2 Beta release, and this is the first time I’ve needed this functionality. So you might be wondering when or why you would ever need this. But there are times when it is very useful, which is why the class exists in the @angular/cdk library.

I ended up needing this functionality because I am using the ngx-gauge library, and one of the options to set in the library is the thickness of the line. You pass it in as an input to the component from the library, and it draws the chart at that thickness. In addition, you pass in a size attribute that sets the height and width. Because of that, I couldn’t change the thickness or the height and width of the chart using CSS media queries. I was left with two options: 1) have two charts on the page, one for bigger screens and one for smaller screens or 2) change the values of those inputs depending on screen size. I chose the latter, because I didn’t want to have to update two charts every time I made a change.

The solution (we’ll go over that in a minute) worked great for me, but I will say that I think the first thing you should try is using CSS media queries before doing this. It would be unnecessary, for example, to keep track of the screen width in your component’s class file, and then apply a class to an HTML element (with ngClass, for example) based on the screen width. Maybe there is a scenario where that is what you need to do, but generally I think you’ll be better off just using CSS media queries to style your components. Having said that, however, if you feel this is the best option for your component, go ahead and do it! I don’t want you to feel bad about using BreakpointObserver.

The Solution

The solution to this problem is pretty simple, thanks to the Angular CDK. The first step is to install the CDK to your project:

npm i @angular/cdk

Next, import the LayoutModule from @angular/cdk/layout to your module. For example, like this in the app.module.ts file:

import { LayoutModule } from '@angular/cdk/layout';

@NgModule({
	imports: [..., LayoutModule, ...]
})
export class AppModule {}

After importing the LayoutModule, inject the BreakpointObserver into your component like any other service you would use:

import { Breakpoint Observer } from '@angular/cdk/layout';

export class MyComponent {
	constructor(private observer: BreakpointObserver) {}
}

Alright, now everything is set up and ready to use. There’s one more thing to figure out, though, before using the service, and that is what break points you want to be notified of. For example, if your site is using Bootstrap and you want to know when you are on the small screen or lower, you would use ‘(max-width: 767px)’. The value you put in the strings is the part of your CSS media query parentheses, parentheses included. You can provide a single string value, or an array of strings.

Once you’ve determined your breakpoints, you have two options to use from the BreakpointObserver. The first one is the isMatched method. It simply evaluates the breakpoints you provided and tells you if the values are matched or not. If you pass in an array to this method, all conditions have to be met for the function to return true.

const matched = this.observer.isMatched('(max-width: 700px)');
// OR
const matched = this.observer.isMatched(['(max-width: 700px)', '(min-width: 500px)']);

The isMatched function works great if you only need to check the first time the page is loaded, or if you only want to check by calling that function occasionally. If you want to be constantly alerted of the matching of your breakpoints, you can use the observe method. The observe method allows you to subscribe and get an update every time the window passes one of the widths that you’ve defined. If you’ve only defined one width to the function, then it will emit a value each time you go above or below that single width. If you provided an array of widths, then each time you cross over the threshold of one of the widths in the array, a value is emitted.

this.observer.observe('(max-width: 700px)').subscribe(result => {
	console.log(this.result);
	// Do something with the result
});

The result that’s outputted here looks like this:

{
	"matches": true | false,
	"breakpoints": {
		"(max-width: 350px)": true | false,
		"(max-width: 450px)": true | false
	}
}

The matches attribute is true if any conditions are met. You can also check for individual breakpoints to see if they have been met if you need.

This function is great if you need to change some layout on the page each time the browser width crosses a certain value. Like I mentioned in my example above, I needed to change the size of a chart and the thickness of the chart depending on the browser width, and since it could be different on landscape vs portrait, I decided to subscribe to the observer method and change it each time it emitted a value.

The last thing to know about the BreakpointObserver is that the CDK provides some built in breakpoints that you can use if you want. They are based on Google’s Material Design specification, and the values are:

  • Handset
  • Tablet
  • Web
  • HandsetPortrait
  • TabletPortrait
  • WebPortrait
  • HandsetLandscape
  • TabletLandscape
  • WebLandscape

You can use them by importing Breakpoints from the CDK’s layout folder:

import { Breakpoints } from '@angular/cdk/layout';

You can then use a breakpoint, like Breakpoint.Handset, in the observe or isMatched functions. They can be used as the only input, or added to the array that is passed in to those functions. You can also mix your own breakpoints with the those built-in ones..

Conclusion

As I said before, you may never use this class or need this functionality. It doesn’t come up very often, but it’s nice to know that this is there and you can reach for it when you do need it. One thing that I want to look into is to see if you could use this function to swap out the template file that the Angular component is using. Again, that would be a rare condition, but you might need it to swap out the content for a mobile experience if it is very different from the desktop experience. I don’t know if you could exactly do that, but you could at least show and hide child components based on the results of the BreakpointObserver.

If you have used this before, make sure to let me know on Twitter or via email how you’ve used it. If you haven’t yet, but need to in the future, also let me know! I like to hear about how other developers use things like this so I can learn more!

Click here to subscribe to the newsletter and be the notified when a new blog post is available!