Ionic + Angular で DRY な共有ナビゲーションコンポーネントを実装する方法

Ionic と Angular を使って共通ナビゲーションを実装する方法を解説。standalone コンポーネントを活用し、コードの重複を避けながら保守性の高いタブバーを実現する DRY な手法を紹介します。

広告ここから
広告ここまで

目次

    モバイルアプリ開発において、複数のページで同じナビゲーション要素を表示することはよくあります。しかし、各ページで同じコードを繰り返し記述するのは保守性の観点から好ましくありません。

    今回は、Ionic と Angular の standalone コンポーネント機能を使って、DRY(Don’t Repeat Yourself)原則に基づいた共有ナビゲーションコンポーネントの実装方法をご紹介します。

    ツールバーコンポーネントを1つにまとめる

    当初、各ページのテンプレートに直接 ion-tab-bar を記述していました。

    <!-- 各ページで同じコードを繰り返し -->
    <ion-tab-bar slot="bottom">
      <ion-tab-button tab="dashboard" href="/forecasts/dashboard">
        <ion-icon aria-hidden="true" name="grid-outline"></ion-icon>
        <ion-label>ダッシュボード</ion-label>
      </ion-tab-button>
      <!-- 他のタブボタン... -->
    </ion-tab-bar>
    

    動作をチェックする面では問題なかったのですが、開発を続ける上で次のような問題が浮上してきました。

    1. コードの重複により同じコードを複数のページで記述する必要がある
    2. 保守性が悪く、変更時に全ページを修正しなければならない
    3. standalone コンポーネントで必要なインポートが不足し、型エラーが発生する
    4. href では Angular ルーティングが動作しない

    共有コンポーネントで問題を解決する

    対策として、Angularの共通コンポーネントを作成する方法を試しました。

    まず、Angular CLI を使って共有コンポーネントを生成します。今回は実験要素が強かったため、テストコードを省略しています。

    npx ng generate component shared/bottom-tabs -- --skip-tests
    

    続いて生成されたsrc/app/shared/bottom-tabs/bottom-tabs.component.tsに共通要素を実装していきます。TSファイル側では、利用したいアイコンや Ionic コンポーネントの登録などを行います。

    import { Component } from '@angular/core';
    import { RouterLink } from '@angular/router';
    import { 
      IonTabBar, 
      IonTabButton, 
      IonIcon, 
      IonLabel 
    } from '@ionic/angular/standalone';
    import { addIcons } from 'ionicons';
    import { 
      gridOutline,
      locationOutline,
      colorPaletteOutline,
      sparklesOutline,
      informationCircleOutline
    } from 'ionicons/icons';
    
    @Component({
      selector: 'app-bottom-tabs',
      standalone: true,
      imports: [
        IonTabBar,
        IonTabButton,
        IonIcon,
        IonLabel,
        RouterLink  // ナビゲーションに必須
      ],
      templateUrl: './bottom-tabs.component.html',
      styleUrl: './bottom-tabs.component.scss'
    })
    export class BottomTabsComponent {
      constructor() {
        // 使用するアイコンを登録
        addIcons({
          'grid-outline': gridOutline,
          'location-outline': locationOutline,
          'color-palette-outline': colorPaletteOutline,
          'sparkles-outline': sparklesOutline,
          'information-circle-outline': informationCircleOutline
        });
      }
    }
    

    続いてsrc/app/shared/bottom-tabs/bottom-tabs.component.htmlにて、共通化したい要素のHTMLを実装します。

    <ion-tab-bar slot="bottom">
      <ion-tab-button tab="dashboard" [routerLink]="['/forecasts/dashboard']">
        <ion-icon aria-hidden="true" name="grid-outline"></ion-icon>
        <ion-label>ダッシュボード</ion-label>
      </ion-tab-button>
    
      <ion-tab-button tab="area" [routerLink]="['/forecasts/area']">
        <ion-icon aria-hidden="true" name="location-outline"></ion-icon>
        <ion-label>地域予報</ion-label>
      </ion-tab-button>
    
      <ion-tab-button tab="rainbow" [routerLink]="['/forecasts/rainbow']">
        <ion-icon aria-hidden="true" name="color-palette-outline"></ion-icon>
        <ion-label>虹予報</ion-label>
      </ion-tab-button>
    
      <ion-tab-button tab="aurora" [routerLink]="['/forecasts/aurora']">
        <ion-icon aria-hidden="true" name="sparkles-outline"></ion-icon>
        <ion-label>オーロラ予報</ion-label>
      </ion-tab-button>
      
      <ion-tab-button tab="introduction" [routerLink]="['/forecasts/introduction']">
        <ion-icon aria-hidden="true" name="information-circle-outline"></ion-icon>
        <ion-label>アプリ紹介</ion-label>
      </ion-tab-button>
    </ion-tab-bar>
    

    共通化するコンポーネントができたので、各ページのコンポーネントでインポートしましょう。

    // 例:weather-forecast.page.ts
    import { BottomTabsComponent } from '../shared/bottom-tabs/bottom-tabs.component';
    
    @Component({
      selector: 'app-weather-forecast',
      standalone: true,
      imports: [
        // 他のインポート...
        BottomTabsComponent
      ],
      // ...
    })
    export class WeatherForecastPage {
      // ...
    }
    

    その後、.htmlファイル側の実装も置き換えます。

    <!-- weather-forecast.page.html -->
    <ion-content>
      <!-- ページコンテンツ -->
    </ion-content>
    
    <app-bottom-tabs></app-bottom-tabs>
    

    まとめ

    Ionic + Angular の standalone コンポーネント機能を活用することで、効率的で保守性の高い共有ナビゲーションコンポーネントを実装できました。Angularはまだ AI にかなり支えてもらいながら使っている状態ですが、共通化などに慣れることで、アプリ開発への投入も進めていきたいなと思います。

    広告ここから
    広告ここまで
    Home
    Search
    Bookmark