How to subscribe for layout changes with Angular CDK

Nikola Tasikj
Web Factory LLC
Published in
5 min readOct 1, 2019

Everyday we are solving challenges about creating responsive content. There are a lot of different screen sizes that we need to take care of. If we want every element to be in the right place on all of those screens, we need to put a lot of effort into our work.

Credits: madisonmott.com

Using media queries with CSS is really helpful and it makes our work easier with catching some breakpoints that we want to handle. But, what if we have some business logic that need to be executed only in several breakpoints? Creating custom classes with separated CSS only to handle this kind of problems will cause mess in our code. This is where Angular CDK jumps in, this CDK have a lot of useful components and tools for easily implementing common application features. One of them is Layout that will help us to build responsive UIs that react to screen-size changes.

First, we need to know what is breakpoint. Imagine that you have some device with 600px width and you’re creating navbar that you want to be with full width for bigger devices, but to be hamburger navbar for your smaller (600px) device. To do this, you need to pick some breakpoint that will act like а border between full width and hamburger navbar. So, if we pick our breakpoint to be 700px, we can create hamburger navbar for all devices below 700px width and full width for devices bigger than 700px. In CSS that looks like:

// Some code for hamburger navbar @media (min-width: 700px) {
//code for >700px devices
}

Note that it’s always better to make code first for smaller devices than to use min-width media, with this you will avoid using !important too often.

Credits: Material.io

How can we do this easily with Angular CDK

Catching every resize and knowing when screen leaves or enter some breakpoint is now really easy with Angular CDK Layout. I will show you example how to create simple service that can be used as global util in your application.

  • Create new Angular project, for this i will use Angular CLI v8.2.1 with NodeJs 10.15.3
  • Install Angular CDK (current version 8.2.1):
npm i @angular/cdk --save
  • To use services from Layout Module, first we need to import it. I will do it in app.module

With importing this module we will have couple custom breakpoints:

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

They are built-in breakpoints based on Google’s Material Design specification, but because we have our customs one, we will create one service where we will keep our breakpoints. This is good to have because it will be easily to add/remove some breakpoints when our app gets bigger and we will respect first solid principle — Single responsibility principle. To create this service we can use ng generate service breakpoints . This CLI command will generate breakpoints.service.ts and breakpoints.service.spec.ts files. So, here we can create one variable breakpoints that will be simple object with а breakpoint value as key and breakpoint name as value, like breakpoint: breakpointName and two methods for getting all breakpoints that we will use in layout service and one for knowing is our breakpoint active. It is good if you export some const for breakpoint names, so when we will use some name we can do CustomBreakpointNames.extraSmall instead of using string extraSmall that can be confusing in some cases. Our code will look like:

Finally after this we can create our layout service on which we will subscribe to get all our custom active breakpoints. Again, we can use CLI command ng generate service layout to easily generate our service. In this service we will keep one variable activeBreakpoints that will be array with all current active breakpoints. In constructor we need to provide our previously created service for breakpoints and breakpoint observer service from Angular CDK layout module.

constructor(private breakpointObserver: BreakpointObserver,
private breakpointService: BreakpointsService) {
}

Also, we will need one method that we will subscribe on to get all updates when some breakpoint is changed. This method will return observable with all active breakpoints in string array.

subscribeToLayoutChanges(): Observable<string[]> {
return this.breakpointObserver
.observe(this.breakpointService.getBreakpoints())
.pipe(map((observeResponse) => this.parseBreakpointsResponse(observeResponse.breakpoints)));
}

In this method we are subscribing to breakpointObserver with .observe() property passing string or array of strings as param and it will return us object with matches (true if some of breakpoints matched) and breakpoints object with all breakpoints that we passed, for each breakpoint it will use breakpoint as key and true/false as value depends of is that breakpoint matched or not. We will need to parse this response if we want easily to check if our requested breakpoint matched or not, so because of that we will create parseBreakpointsResponse where we will push all matched breakpoints names in our activeBreakpoints array. That method can looks like:

parseBreakpointsResponse(breakpoints): string[] {
this.activeBreakpoints = [];

Object.keys(breakpoints).map((key) => {
if (breakpoints[key]) {
this.activeBreakpoints.push(this.breakpointService.getBreakpointName(key));
}
});

return this.activeBreakpoints;
}

In this method for each breakpoint that we passed from response we are checking is his value is true, if it’s true we are pushing our custom name (defined in breakpoints service) in activeBreakpoints array. Additionally in this service we will create isBreakpointActive method.

isBreakpointActive(breakpointName) {
return this.activeBreakpoints.find(breakpoint => breakpoint === breakpointName);
}

We can use this method to simple check if breakpoint that we need is active, but be carefully because this method is not Observable and it can be used only for simple check, for example inside returned response from subscribeToLayoutChanges method.

Finally we have all we need to easily handle breakpoints changes. Now all we need to do is to subscribe to layoutService in components where we need to handle some special case for breakpoints. For this example, just to show you, i will do it in app.component.ts.

With this implementation you can have control for all yours custom breakpoints and it will be so easy to handle some logic in javascript/typescript files.

If you have any questions feel free to ask them in comments. Thank you.

--

--