はじめに
PCなどの大きいディスプレイの時はサイドメニューを常に表示し、モバイル端末などの小さいディスプレイの時はハンバーガーメニューに切り替えてスペースを節約したい。
このように端末のディスプレイサイズに合わせて表示形式が変わるサイドメニューを作成していく。
環境
本記事における実行環境と使用ライブラリについて
$ ng version Angular CLI: 8.3.20 Node: 12.13.1 OS: darwin x64 Angular: 8.2.14 ... animations, common, compiler, compiler-cli, core, forms ... language-service, platform-browser, platform-browser-dynamic ... router Package Version ----------------------------------------------------------- @angular-devkit/architect 0.803.20 @angular-devkit/build-angular 0.803.20 @angular-devkit/build-optimizer 0.803.20 @angular-devkit/build-webpack 0.803.20 @angular-devkit/core 8.3.20 @angular-devkit/schematics 8.3.20 @angular/cdk 7.3.7 @angular/cli 8.3.20 @angular/fire 5.2.3 @angular/material 7.3.7 @ngtools/webpack 8.3.20 @schematics/angular 8.3.20 @schematics/update 0.803.20 rxjs 6.5.3 typescript 3.5.3 webpack 4.39.2
今回やること
レスポンシブなメニューを実装するために、今回やることだが下記となる。
- ディスプレイサイズの検知
- サイドメニューの表示・非表示の切り替え
- ハンバーガーメニューの表示・非表示の切り替え
ダッシュボード画面
最初に画像で貼ったダッシュボード画面(home.component.html
)の実装となる。
ポイントはヘッダー(<app-header>
)とサイドメニュー(<mat-sidenav>
)の実装、そしてディスプレイサイズを検知するisHandset$
の3点である。
<!-- ヘッダー --> <app-header [isHandset$]="isHandset$" (drawerToggled)="drawer.toggle()"></app-header> <mat-sidenav-container class="container"> <!-- サイドメニュー --> <mat-sidenav #drawer [attr.role]="(isHandset$ | async) ? 'dialog' : 'navigation'" [mode]="(isHandset$ | async) ? 'over' : 'side'" class="side-menu" [opened]="!(isHandset$ | async)"> <app-main-side-menu [isHandset]="(isHandset$ | async)" (drawerClosed)="drawer.close()"></app-main-side-menu> </mat-sidenav> <!-- メイン コンテンツ --> <mat-sidenav-content class="main-contents"> <!-- サブサイドメニュー --> <router-outlet></router-outlet> </mat-sidenav-content> </mat-sidenav-container>
isHandset$
Angular CDKのBreakpointObserver
を使用して画面が小さくなったらtrue
を返す、isHandset$
を定義する。
このisHandset$
を使用してハンバーガーメニューを表示したりサイドメニューを消したりして、レスポンシブデザインを実現する。
- home.component.ts
import { Component, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'; import { map } from 'rxjs/operators'; // ...省略 export class HomeComponent { public isHandset$: Observable<boolean> = this.breakpointObserver .observe(Breakpoints.Handset) .pipe(map(result => result.matches)); constructor(private breakpointObserver: BreakpointObserver) {} }
ヘッダー(<app-header>)
ngIf
とisHandset$
を使って画面が小さいときのみ、ハンバーガーメニューを表示している。isHandset$
はObservableを返すので、async
パイプも使用する必要がある。
- header.component.html
<mat-toolbar color="primary"> <div class="main"> <!-- ハンバーガーメニュー --> <button type="button" mat-icon-button *ngIf="(isHandset$ | async)" (click)="toggle()"> <mat-icon>menu</mat-icon> </button> <span class="logo" (click)="signOut()"> <mat-icon class=" header-icon"> note </mat-icon> <span class="title">3Memo</span> </span> <!-- ログアウトボタン(ログインしている時のみ表示される) --> <mat-icon matTooltip="ログアウトする" (click)="signOut()" *ngIf="authenticationService.getUser() | async" class="right-icon">exit_to_app </mat-icon> </div> </mat-toolbar>
サイドメニュー(<mat-sidenav>)
サイドメニューの実装にはAngular MaterialのSidenav
を使用している。
<!-- サイドメニュー --> <mat-sidenav #drawer [attr.role]="(isHandset$ | async) ? 'dialog' : 'navigation'" [mode]="(isHandset$ | async) ? 'over' : 'side'" class="side-menu" [opened]="!(isHandset$ | async)"> <!-- 省略... --> </mat-sidenav>
isHandset$
を使用することでディスプレイが小さくなった時には、attr.role
をdialog
にし、mode
をover
に、opened
をfalse
にしている
参考サイト
Angular Material Responsive Navigation | by Ahmed Abouzied | Medium
https://material.angular.io/cdk/layout/overview