Angular MaterialのSidenavを使用し、コンテンツ部分のページ遷移を行おうとしたときに躓いたのでその備忘録を。

開発環境
  • 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 Materialのインストールに関しては下記記事をご参考ください。

『Angular Flex-Layout』とは

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

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

Angular Flex-Layoutのインストールに関しては下記記事をご参考ください。

Angularのページ遷移

今回の記事で前提となるページ遷移の実装に関しては下記記事をご参考ください。

今回実現したいコト

それでは本題です。

今回実現したいコト
  • ログイン認証など、前提画面を経たうえで、前回までに作成していた画面を表示したい。
  • サイドナビゲーションのメニュークリックで、コンテンツ部分をページ遷移させたい。

ログイン認証の細かな実装は省きますが、会員制サイトをイメージして作っていきます。

使用するプロジェクトは前回と同じです。

遷移先はページ遷移の記事で作成したSamplePageを使用していきます。

とりあえず実装

これまではAppComponentを利用してきましたが、実際の開発現場ではAppComponentを初期ページとしてガンガン使っていくということは無いと思うので、ページを機能別に細かく分けていこうと思います。

新規コンポーネントの作成

作成したコンポーネント
  • Login:初期表示画面
  • Dashboard:ToolbarとSidenavを配置した画面

コンポーネントの作成コマンドは下記の通り。

ng g c (コンポーネント名)

コマンド実行時のカレントディレクトリに作成されることに注意してください。

今回はsrc/app/pages配下に作成しました。

既存コンポーネントからの移行

これまで使用してきたAppComponentから、DashboardComponentへコードを移行します。

長くなっちゃうのでAfterだけ載せていきます。

app-routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { SamplePageComponent } from './pages/sample-page/sample-page.component';
import { LoginComponent } from './pages/login/login.component';
import { DashboardComponent } from './pages/dashboard/dashboard.component';


const routes: Routes = [
  {
    path: 'login',
    component: LoginComponent
  },
  {
    path: 'dashboard',
    component: DashboardComponent
  },
  {
    path: 'sample-page',
    component: SamplePageComponent
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

新たに作成したLoginConponentとDashboardComponentへのルーティング設定を追加しました。

app.component.html

<router-outlet></router-outlet>

これまでメイン画面として利用してきましたが、役割がページ遷移の踏み台のみのため、上記の様な簡素なコードに。

app.component.ts

import { Component } from '@angular/core';
import { Router } from '@angular/router';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = 'sample';

  constructor(
    private router: Router
  ) { this.router.navigate(['login']); }
}

コンストラクタの中でLoginConponentへの遷移を実装しています。

login.component.ts

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {

  constructor(
    private router: Router
  ) { }

  ngOnInit(): void {
    this.router.navigate(['dashboard']);
  }

}

今回はログイン処理までは実装しないので、描画の時点でDashboardComponentへと遷移させます。

dashboard.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 (click)="onMenuItemClicked()">
          <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>

雑なコードですが、サイドナビゲーションの「メニュー1」がクリックされたときにonMenuItemClickedメソッドが呼ばれるようにしています。

dashboard.component.ts

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';

@Component({
  selector: 'app-dashboard',
  templateUrl: './dashboard.component.html',
  styleUrls: ['./dashboard.component.scss']
})
export class DashboardComponent implements OnInit {

  constructor(
    private router: Router
  ) { }

  ngOnInit(): void {
  }

  onMenuItemClicked() {
    this.router.navigate(['sample-page']);
  }

}

onMenuItemClickedメソッドではSamplePageへの遷移を行うように実装しました。

dashboard.component.scss

@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);
    }
}

AppComponentのSCSSからカトペです。かとちゃんぺではありません。

表示結果

初期表示がこちら。

サイドナビゲーションのメニュー1をクリックしてSamplePageへ遷移してみると…。

コンテンツ部分に表示したかったのですが、ページ丸ごと遷移してしまいました。これはあかん。

修正版実装

原因はルーティング設定。

DashboardとSamplePageを同じ階層に定義しているため、丸ごとの遷移になってしまうようです。

app-routing.module.ts(修正版)

というコトでルーティング設定を見直していきます。

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { SamplePageComponent } from './pages/sample-page/sample-page.component';
import { LoginComponent } from './pages/login/login.component';
import { DashboardComponent } from './pages/dashboard/dashboard.component';


const routes: Routes = [
  {
    path: 'login',
    component: LoginComponent
  },
  {
    path: 'dashboard',
    component: DashboardComponent,
    children: [
      {
        path: 'sample-page',
        component: SamplePageComponent
      }
    ]
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

先ほどは同じ階層に定義していたSamplePageをDashboardの子要素として設定しました。

dashboard.component.ts(修正版)

ルーティング設定の変更に伴い、遷移の処理も変更します。

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';

@Component({
  selector: 'app-dashboard',
  templateUrl: './dashboard.component.html',
  styleUrls: ['./dashboard.component.scss']
})
export class DashboardComponent implements OnInit {

  constructor(
    private router: Router
  ) { }

  ngOnInit(): void {
  }

  onMenuItemClicked() {
    this.router.navigate(['dashboard/sample-page']);
  }

}

Router.navigateメソッドの引数を、Dashboard配下のSamplePageを指すように変更しました。

表示結果(修正版)

初期表示は先ほどと変わりありません。

続いてサイドナビゲーションのメニュー1をクリックしてみると…。

想定通り、コンテンツ部分がSamplePageへと遷移しました。

まとめ

Sidenavコンテンツ部分の遷移を実装するには
  • ルーティング設定で遷移先ページをSidenav実装ページの子要素として設定する。
  • 遷移の際は「(Sidenav実装ページ)/(遷移先ページ)」を指定する。

サイドナビゲーションを利用する機会が無かったこともあり、なかなかてこずりました。いい経験ですね。

…ちなみに仕事で出くわしたプロジェクトではいまだにルーティング周りを頭の中で整理できていません。

今後もこの辺りは重点的に勉強していきたいところです。基本的なトコロなんですけどね~、難しい。

以上です。

スポンサーリンク