ふるてつのぶろぐ

福岡在住のエンジニアです。

写真提供:福岡市

Angular7 で Web アプリを作ろう - mat-menu

今日すること

こんにちは、ふるてつです。
今回のテーマもAngular Materialで、<mat-menu>のお話です。
以前わたしがbootstrapで作っていたメニューと同じものをmaterialで作ろうと思います。
f:id:tetsufuru:20190519232232p:plain:w500
メニューは上記のような感じです(緑色の部分です)

mat-menu関連のリファレンスについて

リファレンスはこちらです。https://material.angular.io/components/menu/overview f:id:tetsufuru:20190519173040p:plain:w500
リファレンスを参考にして作っていきます。
わたしの場合はヘッダー用のコンポーネントを作っていて、その中にまず<mat-toolbar>を配置しています。 そしてその中に<mat-menu>mat-buttonを動的に入れています。
サインアウトなど必ず表示するアイコンなどは固定で配置します。

ヘッダー用のコンポーネントについて

まずヘッダーのhtmlですが、下記のようになります。
header.component.html

<div *ngIf="this.router.url!=='/signIn'">
  <mat-toolbar class="toolbar mat-elevation-z8">
    <mat-toolbar-row class="toolbar">
      <button mat-icon-button (click)="onToggleSidenav()">
        <mat-icon>menu</mat-icon>
      </button>
      <button mat-button routerLink="/account-setting" class="btnMenu">
        <div class="systemName">Sanrokumaru</div>
      </button>

      <div class="toolBarMenu">
        <ng-container *ngFor="let item of availableMenuListDtoLists">
          <button mat-button [matMenuTriggerFor]="appMenu" class="btnMenu">
            <div class="btnMenu">{{ item.propertyId | translate }}</div>
          </button>
          <mat-menu #appMenu="matMenu">
            <ng-container *ngFor="let subitem of item.availableMenuDto">
              <button mat-menu-item routerLink="/{{subitem.apiName}}">
                {{ subitem.propertyId | translate }}
              </button>
            </ng-container>
          </mat-menu>
        </ng-container>
      </div>

      <button mat-button routerLink="/account-setting" class="btnAccount">
        <mat-icon>account_circle</mat-icon>
      </button>

      <button mat-button routerLink="/account-setting" class="btnSignOut">
        <mat-icon>exit_to_app</mat-icon>
      </button>

    </mat-toolbar-row>
  </mat-toolbar>
</div>

順に<mat-icon>menu</mat-icon>はまずsidenav表示用のメニューアイコンです。
(ただし画面サイズが大きい時は表示されません)
次に<div class="systemName">Sanrokumaru</div>はシステム名の部分。
そして<ng-container *ngFor="let item of availableMenuListDtoLists">のところがメニューを動的に作る箇所です。
さらに下の<ng-container *ngFor="let subitem of item.availableMenuDto">のところはサブメニューを作る箇所で、メニューに対してネストして繰り返しています。
残りの<mat-icon>account_circle</mat-icon><mat-icon>exit_to_app</mat-icon>がそれぞれアカウントの設定と、サインアウトボタンです。

次にtsファイルの内容を書きます。
header.component.tsの内容

import { Component, OnInit, Output, EventEmitter } from '@angular/core';
import { Router } from '@angular/router';
import { AccountService } from 'src/app/service/common/account/account.service';
import { AvailableMenuListDto } from 'src/app/entity/dto/available-menu-list-dto';

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.css']
})
export class HeaderComponent implements OnInit {
  // 親コンポーネントとの連係
  @Output() public sidenavToggle = new EventEmitter();

  // メニュー
  public availableMenuListDtoLists: AvailableMenuListDto[];

  constructor(
    private accountService: AccountService,
    public router: Router
  ) { }

  ngOnInit() {
    // メニューを取得する。
    this.getAvailableMenu();
  }
  /**
   * メニューを取得する。
   */
  private getAvailableMenu(): void {
    this.accountService.getAvailableMenu()
      .subscribe(availableMenuListDtoLists => this.availableMenuListDtoLists = availableMenuListDtoLists);
  }
  /**
   * イベントを発生させる。
   */
  public onToggleSidenav = () => {
    this.sidenavToggle.emit();
  }
}

public availableMenuListDtoLists: AvailableMenuListDto[];がサーバから取得したメニューの内容を格納する変数です。
これは初期表示時にgetAvailableMenu();で取得します。
以前sidenavで使用したものと同じ変数を使用します。https://tetsufuru.hatenablog.com/entry/2019/05/07/212627
public onToggleSidenav = () => {の周辺はsidenavを関連のメソッドです。

レスポンシブ対応

以前作ったsidenavだけで十分役に立つので、あえてメニューはなくても良いかなと思いましたが一旦、bootstrapのように画面サイズが小さい時はsidevanのみを表示しPCなどサイズが大きい時はメニューのみを表示するように切り替えました。
そのあたりの切り替えはわたしはMedia Queriesでおこなっています。

header.component.cssの内容(Media Queriesの部分のみ抜粋)

/* Tablet or PC  */
@media only screen and (min-width: 641px) {
  .mat-icon-button {
    display: none;
  }
}

/* SmartPhone */
@media screen and (max-width: 640px) {
  .toolBarMenu {
    display: none;
  }
}

640px以下の時はメニューが表示されないようにして、641px以上の時はsidenavが表示されないようにしています。

動かしてみる

では動かしてみます。 下のように動的に作った部分やそのサブメニューも出るようになりました。
f:id:tetsufuru:20190520000928p:plain:w500
画面サイズが小さい場合は下記のようにメニューアイコンに切り替わります。
f:id:tetsufuru:20190520001521p:plain:w400
最後にsidenavをクリックすると下記のようにサブメニューが表示されました。
f:id:tetsufuru:20190520002237p:plain:w400

今日の感想

今日は以前書いたsidenavの続きになりますが、mat-menuを表示する方法でした。
今回はそれほど苦労はしませんでした、sidenavで少し慣れていたからと思います。
せっかくマテリアルで作っているので、わざわざbootstrapと同じようなメニュー切替にする必要はなかったのですが、 せっかくsidenabmat-menuの両方を作ったので、一旦はこのまま使ってみようと思います。

では、今日もお疲れ様でした。