大田区から発信するゆるゆる日記

主にITエンジニアに関する備忘録日記。たまに趣味も。何か不備があればコメント頂けると幸いです。Twitterアカウント https://twitter.com/ryuzan03

【Ionic/Angular】Tabsテンプレートで作ったIonicアプリにSideBarを追加する

※下記の内容に不備がありましたら、コメント頂けると幸いです。また、下記の内容をご使用頂ける場合は自己責任でお願いします。

【目次】

【2020/5/7 追記】
今回の記事の別解を書きました。別解の方がスマートなので、個人的には別解で実装することをオススメします。
【Ionic/Angular】Tabsテンプレートで作ったIonicアプリにSideBarを追加する(別解編) - 大田区から発信するゆるゆる日記

【2020/4/28 追記】
下記の内容では遷移後のページでアクション(イベント)を起こしても反応しないことが分かりました。この記事の内容が多いため、エラー解決方法については別の記事に書かせていただきます。詳しくはこちら【Ionic/Angular】Tabsテンプレートで作ったIonicアプリにSideBarを追加する(エラー対応編) - 大田区から発信するゆるゆる日記をご確認ください。

概要

Tabsテンプレートで作ったIonicアプリにSidebarを追加してみました。意外に躓いたので備忘録にします。

あと恐らくこれはベストプラクティスではないと思います。「こうした方がいいよ」とかあれば、コメントいただければとても嬉しいです。

完成イメージと参考動画

完成イメージはこちらです。
デスクトップではslidebar、スマホではtabsが表示されるようにします。
Ionic Frameworkを用いたデスクトップレイアウトを考える - Qiita

slidebarを追加する際に参考にした動画です。
How to Combine Ionic 4 Tabs and Side Menu Navigation - YouTube


Tabsテンプレートで作ったIonicアプリにSideBarを追加する

ポイント

ポイントはMenuPageのion-split-paneにcontentIdを追加することとmenu-routingのややこしいルーティングです。

contentIdの追加は参考にした動画では追加されていないので忘れがちです。

Ionicアプリを作成する。

$ ionic start project tabs --type=angular

MenuPageを追加する。
このMenuPageがサイドバーになります。

project $ ionic g page menu

app-routing.module.ts

...
const routes: Routes = [
  {
    path: '',
    loadChildren: () => import('./menu/menu.module').then( m => m.MenuPageModule)
  }
];
...

menu.page.html

<ion-split-pane contentId="content">
  <ion-menu contentId="content">

    <ion-header>
      <ion-toolbar color="primary">
        <ion-title>MENU</ion-title>
      </ion-toolbar>
    </ion-header>

    <ion-content>
      <ion-list>
        <ion-menu-toggle auto-hide="false" *ngFor="let p of pages">
          <ion-item [routerLink]="p.url" routerDirection="root" [class.active-item]="selectedPath.startsWith(p.url)">
            <ion-label>
              {{p.title}}
            </ion-label>
          </ion-item>
        </ion-menu-toggle>
      </ion-list>
    </ion-content>
  </ion-menu>

  <ion-router-outlet id="content"></ion-router-outlet>
</ion-split-pane>

menu.page.ts

...
export class MenuPage {

  pages = [
    {
      title: 'tab1',
      url: '/menu/tab1'
    },
    {
      title: 'tab2',
      url: '/menu/tab2'
    },
    {
      title: 'tab3',
      url: '/menu/tab3'
    },
  ];

  selectedPath = '';

  constructor(private router: Router) {
    this.router.events.subscribe((event: RouterEvent) => {
      if (event && event.url) {
        this.selectedPath = event.url;
      }
    });
   }
}

menu-routing.module.ts

...
const routes: Routes = [
  {
    path: 'menu',
    component: MenuPage,
    children: [
      {
        path: 'tab1',
        children: [
          {
            path: '',
            loadChildren: () =>
            import('../tab1/tab1.module').then(m => m.Tab1PageModule),
          },
          {
            path: 'tab1',
            redirectTo: '',
            pathMatch: 'full'
          },
        ]
      },
      {
        path: 'tab2',
          children: [
            {
              path: '',
              loadChildren: () =>
              import('../tab2/tab2.module').then(m => m.Tab2PageModule),
            },
            {
              path: 'tab2',
              redirectTo: '',
              pathMatch: 'full'
            }
          ]
      },
      {
        path: 'tab3',
        children: [
          {
            path: '',
            loadChildren: () =>
              import('../tab3/tab3.module').then(m => m.Tab3PageModule)
          },
          {
            path: 'tab3',
            redirectTo: '',
            pathMatch: 'full'
          }
        ]
      },
      {
        path: '',
        redirectTo: '/menu/tab1',
        pathMatch: 'full'
      }
    ]
  },
...


タブの表示/非表示を実装する

TabsPageを利用してタブの表示/非表示を実装します。
今回はTabsPageをコンポーネント化してSharedModuleに登録し、各tabに追加していきます。

SharedModuleを作成する

project $ ionic g module shared

tabs.component.ts

...
@Component({
  selector: 'ls-tabs',
  templateUrl: 'tabs.component.html',
  styleUrls: ['tabs.component.scss']
})
export class TabsComponent {
  constructor() {}
}
...

tabs.component.scss

@media screen and (min-width: 992px) {
  ion-tab-bar {
    display: none;
  }
}

shared.module.ts

...
@NgModule({
  imports: [
    CommonModule,
    IonicModule
  ],
  declarations: [TabsComponent],
  exports: [TabsComponent],
})
...

tab1/tab2/tab3.page.html

...
</ion-content>
<ls-tabs></ls-tabs>

tab1/tab2/tab3.module.ts

imports: [
  ...
  SharedModule,
  ...
],


タブのルーティングを紐付ける

menu-routing.module.ts

...
      {
        path: 'tab3',
        children: [
          {
            path: '',
            loadChildren: () =>
              import('../tab3/tab3.module').then(m => m.Tab3PageModule)
          },
          {
            path: 'tab3',
            redirectTo: '',
            pathMatch: 'full'
          }
        ]
      },
      {
        path: 'tab2/tab1',
        redirectTo: 'tab1',
        pathMatch: 'full'
      },
      {
        path: 'tab3/tab1',
        redirectTo: 'tab1',
        pathMatch: 'full'
      },
      {
        path: 'tab1/tab2',
        redirectTo: 'tab2',
        pathMatch: 'full'
      },
      {
        path: 'tab3/tab2',
        redirectTo: 'tab2',
        pathMatch: 'full'
      },
      {
        path: 'tab1/tab3',
        redirectTo: 'tab3',
        pathMatch: 'full'
      },
      {
        path: 'tab2/tab3',
        redirectTo: 'tab3',
        pathMatch: 'full'
      },
      {
        path: '',
        redirectTo: '/menu/tab1',
        pathMatch: 'full'
      }
    ]
  },
  {
    path: '',
    redirectTo: '/menu/tab1',
    pathMatch: 'full'
  }
];
...


今後に向けて

今回はTabsテンプレートを尊重するあまりルーティングがややこしくなってしまいました。
各タブにURLを割り当てていたらもっと簡単になってたのかな...

もっとスマートな方法を知っている方がいましたらコメントお願いします!

参考資料

素晴らしい記事に感謝致します。
ion-split-pane - Ionic Framework 日本語ドキュメンテーション
Angular Lazy LoadingでつまずいちゃうNgModuleのコンポーネント登録 - Qiita
Ionic Frameworkを用いたデスクトップレイアウトを考える - Qiita
How to Combine Ionic 4 Tabs and Side Menu Navigation - YouTube