【Angular】Angular MaterialのToolbarとSidenavを併用するときの注意点

Angular公式のドキュメントページをマネしようとしてちょっと苦戦したので、その備忘録です。

開発環境
  • OS:Windows10 Pro
  • Node.js:v12.16.3
  • npm:6.14.4
  • Angular CLI:9.1.4
  • Angular Material:9.2.4
  • Angular Flex-Layout:9.0.0-beta.31

『Angular Material』とは

Angularの開発元であるGoogleがAngular公式コンポーネントとして開発しているパッケージです。

よりリッチなUIを作成する為の部品を様々提供してくれる便利な奴です。

今回はAngular Materialの「Toolbar」と「Sidenav」というコンポーネントを使って、Angular公式ドキュメントページのようなUIを実装しようと試みました。

『Angular Flex-Layout』とは

Angular開発の際にデザインの実装を助けてくれる、CSSの拡張みたいなパッケージです。

レスポンシブにも対応しており、従来よりも少ないコードでの実装が可能です。

ToolbarとSidenavの実装

まずは一番最初に行った実装から。

今回もプロジェクト作成と同時に作成される、AppComponetを使っていきます。

app.module.ts

まずは下記の通りコンポーネントのモジュールをインポートします。

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FlexLayoutModule } from '@angular/flex-layout';        ★追加
import { MatButtonModule } from '@angular/material/button';     ★追加
import { MatIconModule } from '@angular/material/icon';         ★追加
import { MatListModule } from '@angular/material/list';         ★追加
import { MatSidenavModule } from '@angular/material/sidenav';   ★追加
import { MatToolbarModule } from '@angular/material/toolbar';   ★追加

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,
    FlexLayoutModule,       ★追加
    MatButtonModule,        ★追加
    MatIconModule,          ★追加
    MatListModule,          ★追加
    MatSidenavModule,       ★追加
    MatToolbarModule,       ★追加
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

ToolbarとSidenavの他に、ButtonとIconとListというモジュールも追加していることにご注意ください。

ButtonとIconはツールバー上のサイドナビゲーション開閉用ボタンに、Listはナビゲーションアイテムを表示するために使用します。

app.component.html

<mat-toolbar color="primary">
  <span>
    <button mat-icon-button (click)="sidenav.toggle()">
      <mat-icon>menu</mat-icon>
    </button>
  </span>
  <span>My Application</span>
</mat-toolbar>
<mat-sidenav-container fxFlexFill>
  <mat-sidenav mode="side" opened #sidenav>
    <mat-nav-list>
      <mat-list-item>
        <span>メニュー1</span>
      </mat-list-item>
      <mat-list-item>
        <span>メニュー2</span>
      </mat-list-item>
      <mat-list-item>
        <span>メニュー3</span>
      </mat-list-item>
      <mat-list-item>
        <span>メニュー4</span>
      </mat-list-item>
    </mat-nav-list>
  </mat-sidenav>
  <mat-sidenav-content>
    <router-outlet></router-outlet>
  </mat-sidenav-content>
</mat-sidenav-container>

表示結果

無事、動作を確認できた、と思いきや赤矢印の箇所にご注目ください。

コンテンツ部分には何も表示していないにもかかわらず、スクロールバーが出現しています。

これ、ちょっと気持ち悪いですよね。

余白の原因

色々触ってみた感じ、コンテンツ部分に設定している「fxFlexFill」はその役割を果たしているのですが、Toolbarを考慮せずに画面いっぱいまで広がっている模様。

解決策

コチラのページが参考になりました。

app.component.html(修正後)

<mat-toolbar color="primary">
  <span>
    <button mat-icon-button (click)="sidenav.toggle()">
      <mat-icon>menu</mat-icon>
    </button>
  </span>
  <span>My Application</span>
</mat-toolbar>
<div style="height: calc(100vh - 64px);"> ★追加
  <mat-sidenav-container fxFlexFill>
    <mat-sidenav mode="side" opened #sidenav>
      <mat-nav-list>
        <mat-list-item>
          <span>メニュー1</span>
        </mat-list-item>
        <mat-list-item>
          <span>メニュー2</span>
        </mat-list-item>
        <mat-list-item>
          <span>メニュー3</span>
        </mat-list-item>
        <mat-list-item>
          <span>メニュー4</span>
        </mat-list-item>
      </mat-nav-list>
    </mat-sidenav>
    <mat-sidenav-content>
      <router-outlet></router-outlet>
    </mat-sidenav-content>
  </mat-sidenav-container>
</div>

Sidenavをdivタグで囲み、高さを指定しました。

表示結果(修正後)

今度こそ、想定通りの表示結果を得られました。

さらに考慮すべき点

しかしこれでオールオッケー!というコトではなく、もう一つ考慮すべき点があります。

それが、レスポンシブ対応。

上記はChromeの開発者ツールを使って、スマホでの表示を行ったものです。

赤丸の点に注目して頂くと、少しサイドナビゲーションの境界線が切れているのが分かるかと思います。

修正版HTMLでハードコーディングした64ピクセルはブラウザ表示時のツールバーの大きさなので、スマホに対応する場合はスマホ表示時のツールバーの大きさを指定する必要があります。

せっかくなのでついでに試してみましょう。

app.component.scss(レスポンシブ版)

レスポンシブ対応にあたって、SCSSへの記載を追加していきます。

Flex-Layoutでも出来るのかもしれませんが、ちょっと調べた感じだと今回は対応できなさそうだったので…。

@media screen and (max-width:639px) {
    /*スマホ用のcssを記述*/
    .mobile-sidenav{
        height: calc(100vh - 56px);
    }
}

@media only screen and (min-width:640px) and (max-width:1023px) {
    /*tablet用のcssを記述*/
    .mobile-sidenav{
        height: calc(100vh - 56px);
    }
}

@media screen and (min-width:1024px) {
    /*pc用のcssを記述*/
    .mobile-sidenav{
        height: calc(100vh - 64px);
    }
}

一つのファイルでレスポンシブ対応が出来る書き方を採用しています。便利ですね。

コチラのレスポンシブ対応方法に関しては以下のサイトを参考にさせて頂きました。

app.component.html(レスポンシブ版)

<head>
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
</head>
<mat-toolbar color="primary">
  <span>
    <button mat-icon-button (click)="sidenav.toggle()">
      <mat-icon>menu</mat-icon>
    </button>
  </span>
  <span>My Application</span>
</mat-toolbar>
<div class="mobile-sidenav">
  <mat-sidenav-container fxFlexFill>
    <mat-sidenav mode="side" opened #sidenav>
      <mat-nav-list>
        <mat-list-item>
          <span>メニュー1</span>
        </mat-list-item>
        <mat-list-item>
          <span>メニュー2</span>
        </mat-list-item>
        <mat-list-item>
          <span>メニュー3</span>
        </mat-list-item>
        <mat-list-item>
          <span>メニュー4</span>
        </mat-list-item>
      </mat-nav-list>
    </mat-sidenav>
    <mat-sidenav-content>
      <router-outlet></router-outlet>
    </mat-sidenav-content>
  </mat-sidenav-container>
</div>

ハードコーディングをしていた箇所にスタイルを充てただけです。

表示結果(レスポンシブ版)

PC版

モバイル版

iPhone

Android(Galaxy)

タブレット(iPad)

ちょっと見えにくいかもしれませんが、デバイスによらず想定通りの表示となっています。

最後に

ちょっと長くなってしまいましたが、Angular MaterialのToolbarとSidenavを併用する際には、Sidenavの高さに注意が必要です。

Sidenavの高さを計算するには、Toolbarの高さを実装者が意識する必要があります。

指定の仕方もちょっと原始的なので、もうちょっとうまい方法が見つかったらまた書きます。

以上です。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください