While using the Angular BreakpointObserver
in conjunction with Angular Material
, I try to use Material Design-spec breakpoints (they're defined in Breakpoints
, for example Breakpoints.LandscapePortrait
, Breakpoints.HandsetPortrait
).
The breakpointObserver works, except for when I freshly load the page. The breakpointObserver is only triggered after a change is observed, but considering it's a fresh load, there is no change yet. This means the initial viewport is not yet evaluated against the breakpoints. I've tried using a single BreakpointObserver.isMatched
in OnInit
, but this does not seem to take any effect.
I included BreakpointObserver
, Breakpoints
and MediaMatcher
into a shared service, to which I subscribe in all the components that need to "listen" to the breakpoints.
My question is: how can I make sure that the breakpoints are evaluated before the first viewport-change (which may not happen at all, if the user does not resize the window/change device orientation)?
Here's the code of my shared.service.ts
:
import {Component, OnDestroy, OnInit, Injectable,Input,Output,EventEmitter} from '@angular/core';
import {BreakpointObserver, Breakpoints, MediaMatcher} from '@angular/cdk/layout';
@Injectable()
export class SharedService implements OnDestroy, OnInit {
public isCollapsed = false;
public isOpen = false;
public isMobileView = false;
public isTabletView = false;
private breakpointObserver: BreakpointObserver;
@Output() mediaChange = new EventEmitter();
constructor(breakpointObserver: BreakpointObserver) {
this.breakpointObserver = breakpointObserver;
// check if the viewport matches Material Design-spec handset displays
this.breakpointObserver.observe([
Breakpoints.HandsetPortrait
]).subscribe(result => {
if (result.matches) {
this.isMobileView = true;
this.isTabletView = false;
this.isOpen = false;
this.isCollapsed = false;
}
else {
this.isMobileView = false;
this.isOpen = true;
this.isCollapsed = false;
}
this.mediaChanged();
});
// check if the viewport matches Material Design-spec tablet displays
this.breakpointObserver.observe([
Breakpoints.TabletPortrait
]).subscribe(result => {
if (result.matches) {
this.isTabletView = true;
this.isMobileView = false;
this.isOpen = true;
this.isCollapsed = true;
}
else {
if(!this.isMobileView){
this.isOpen = true;
}
this.isTabletView = false;
this.isCollapsed = false;
}
this.mediaChanged();
});
}
mediaChanged() {
this.mediaChange.emit({
"isMobileView" : this.isMobileView,
"isTabletView" : this.isTabletView,
"isCollapsed" : this.isCollapsed,
"isOpen" : this.isOpen
});
}
ngOnInit() {
// MY ATTEMPT
// Running the same checks as the observer, but this time on init(?)
// does not seem to take any effect
if(this.breakpointObserver.isMatched([
Breakpoints.HandsetPortrait
])){
this.isMobileView = true;
this.isTabletView = false;
this.isOpen = false;
this.isCollapsed = false;
}
if(this.breakpointObserver.isMatched([
Breakpoints.TabletPortrait
])){
this.isTabletView = true;
this.isMobileView = false;
this.isOpen = true;
this.isCollapsed = true;
}
this.mediaChanged();
}
}
The first step is to install the CDK to your project: Next, import the LayoutModule from @angular/cdk/layout to your module. For example, like this in the app.module.ts file: After importing the LayoutModule, inject the BreakpointObserver into your component like any other service you would use: Alright, now everything is set up and ready to use.
The breakpointObserver works, except for when I freshly load the page. The breakpointObserver is only triggered after a change is observed, but considering it's a fresh load, there is no change yet. This means the initial viewport is not yet evaluated against the breakpoints.
They are based on Google's Material Design specification, and the values are: You can use them by importing Breakpoints from the CDK's layout folder: You can then use a breakpoint, like Breakpoint.Handset, in the observe or isMatched functions.
The breakpointObserver is only triggered after a change is observed, but considering it's a fresh load, there is no change yet. This means the initial viewport is not yet evaluated against the breakpoints. I've tried using a single BreakpointObserver.isMatched in OnInit, but this does not seem to take any effect.
set a local variable to false and change it depending on the value in the subscription.
import { Component, OnInit } from '@angular/core';
import { BreakpointObserver, Breakpoints, BreakpointState } from '@angular/cdk/layout';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-main-menu',
templateUrl: './main-menu.component.html',
styleUrls: ['./main-menu.component.css']
})
export class MainMenuComponent implements OnInit {
isHandset = false;
constructor(private breakpointObserver: BreakpointObserver, private route: ActivatedRoute) {}
ngOnInit() {
this.breakpointObserver.observe(Breakpoints.Handset)
.subscribe((state: BreakpointState) => {
if (state.matches) {
this.isHandset = true;
} else {
this.isHandset = false;
}
});
}
}
Template code:
<mat-sidenav-container class="sidenav-container">
<mat-sidenav #drawer class="sidenav" fixedInViewport
[attr.role]="isHandset ? 'dialog' : 'navigation'"
[mode]="isHandset ? 'over' : 'side'"
[opened]="isHandset === false">
<mat-toolbar>Menu</mat-toolbar>
<mat-nav-list>
<a mat-list-item href="#">Link 1</a>
<a mat-list-item href="#">Link 2</a>
<a mat-list-item href="#">Link 3</a>
</mat-nav-list>
</mat-sidenav>
<mat-sidenav-content>
<mat-toolbar color="primary">
<button
type="button"
aria-label="Toggle sidenav"
mat-icon-button
(click)="drawer.toggle()"
*ngIf="isHandset">
<mat-icon aria-label="Side nav toggle icon">menu</mat-icon>
</button>
<span>timplaw-dotcom</span>
</mat-toolbar>
<!-- Add Content Here -->
<router-outlet></router-outlet>
</mat-sidenav-content>
</mat-sidenav-container>
Reference: https://alligator.io/angular/breakpoints-angular-cdk/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With