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();
}
}
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/