Ionicのion-tabsのタブ型ナビゲーションのベストプラクティス

2018.11.14

概要

IonicのTabs(ion-tabs)を使ってスマホアプリを開発する上で、どのようなコンポーネントを作って、それらの遷移をどのように実装するのが理想かを検討してみた。今回作りたいUIは以下のようなイメージ。

Ionicのion-tabsのナビゲーションのベストプラクティス

環境

  • macOS Mojave 10.14(18A391)
  • iTerm
  • WebStorm
  • ndenv 0.4.0-4-ga339097
  • Node.js v7.1.0
  • Ionic 3.19.0

Ionic CLIのテンプレートを使ってタブバーを持ったアプリを作る



$ ionic start

? What would you like to name your project: TabNavigationSample

? What starter would you like to use:
❯ tabs ............... ionic-angular A starting project with a simple tabbed interface
  blank .............. ionic-angular A blank starter project
  sidemenu ........... ionic-angular A starting project with a side menu with navigation in the content area
  super .............. ionic-angular A starting project complete with pre-built pages, providers and best practices for Ionic development.
  conference ......... ionic-angular A project that demonstrates a realworld application
  tutorial ........... ionic-angular A tutorial based project that goes along with the Ionic documentation
  aws ................ ionic-angular AWS Mobile Hub Starter

? Would you like to integrate your new app with Cordova to target native iOS and Android? (y/N) N

? Install the free Ionic Pro SDK and connect your app? (Y/n) n

この時点でのディレクトリ構成は以下のような感じ。



$ cd TabNavigationSample
$ tree -I node_modules .

.
├── ionic.config.json
├── package.json
├── src
│   ├── app
│   │   ├── app.component.ts
│   │   ├── app.html
│   │   ├── app.module.ts
│   │   ├── app.scss
│   │   └── main.ts
│   ├── assets
│   │   ├── icon
│   │   │   └── favicon.ico
│   │   └── imgs
│   │       └── logo.png
│   ├── index.html
│   ├── manifest.json
│   ├── pages
│   │   ├── about
│   │   │   ├── about.html
│   │   │   ├── about.scss
│   │   │   └── about.ts
│   │   ├── contact
│   │   │   ├── contact.html
│   │   │   ├── contact.scss
│   │   │   └── contact.ts
│   │   ├── home
│   │   │   ├── home.html
│   │   │   ├── home.scss
│   │   │   └── home.ts
│   │   └── tabs
│   │       ├── tabs.html
│   │       └── tabs.ts
│   ├── service-worker.js
│   └── theme
│       └── variables.scss
├── tsconfig.json
└── tslint.json

開発用のローカルサーバーを立ててブラウザで開いてみる。



$ ionic serve

Ionicのion-tabsのナビゲーションのベストプラクティス

「Home」「About」「Contact」の3つのタブを持つIonicアプリケーションが作成できた。

コンポーネント間のナビゲーションを検証するためにコードを変更する

今回はIonicの基本的な使い方の手順というより、Tabsを使ったナビゲーションのベストプラクティスに関する考察なので、詳細な変更点や実装の説明は省略する。ソースコードはGitHubに公開予定。

以下のようなコンポーネントの構成を作ってみる。

Ionicのion-tabsのナビゲーションのベストプラクティス

エンティティ(ユーザーとか、記事とか)ベースでディレクトリを分けて、主要な機能ごとにコンポーネント名とした。このようなディレクトリ構成はWebアプリケーションフレームワークのRuby on Railsを参考にした。

NgModuleでのインポートが若干複雑になりそうなのでそこは後で詳しく触れる。

実装した後のディレクトリ構成は以下の通り。



$ tree -I "node_modules|www" .

.
├── ionic.config.json
├── package.json
├── src
│   ├── app
│   │   ├── app.component.ts
│   │   ├── app.html
│   │   ├── app.module.ts
│   │   ├── app.scss
│   │   ├── components.ts
│   │   └── main.ts
│   ├── assets
│   │   ├── icon
│   │   │   └── favicon.ico
│   │   └── imgs
│   │       └── logo.png
│   ├── index.html
│   ├── manifest.json
│   ├── models
│   │   ├── article.model.ts
│   │   └── user.model.ts
│   ├── navigations
│   │   └── tabs
│   │       ├── tabs.component.html
│   │       └── tabs.component.ts
│   ├── pages
│   │   ├── article
│   │   │   ├── index
│   │   │   │   ├── index.component.html
│   │   │   │   ├── index.component.scss
│   │   │   │   └── index.component.ts
│   │   │   └── show
│   │   │       ├── show.component.html
│   │   │       ├── show.component.scss
│   │   │       └── show.component.ts
│   │   └── user
│   │       ├── index
│   │       │   ├── index.component.html
│   │       │   ├── index.component.scss
│   │       │   └── index.component.ts
│   │       └── show
│   │           ├── show.component.html
│   │           ├── show.component.scss
│   │           └── show.component.ts
│   ├── service-worker.js
│   └── theme
│       └── variables.scss
├── tsconfig.json
└── tslint.json

エンティティごとにディレクトリを分けたことで、非常に見通しを良くすることができた。 ArticleとUserに関して、一覧画面と詳細画面がある構成が全く同じなので、今回はArticleモデルに着目して検討を進める。

一覧から詳細へのナビゲーション

Ionicのion-tabsのナビゲーションのベストプラクティス

一覧画面(src/pages/article/index/index.component.ts)から詳細画面(src/pages/article/show/show.component.ts)へのナビゲーションを確認する。



import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import { ShowPageComponent } from '../show/show.component';
import { Article } from '../../../models/article.model';

@Component({
  selector: 'page-article-index',
  templateUrl: './index.component.html'
})
export class IndexPageComponent {

  articles: Article[] = [
    new Article(1, 'Tabsを試してみる', 'Tabsの紹介記事です。'),
    new Article(2, 'FabButtonを試してみる', 'FabButtonの紹介記事です。'),
    new Article(3, 'InfiniteScrollを試してみる', 'InfiniteScrollの紹介記事です。'),
    new Article(4, 'LoadingControllerを試してみる', 'LoadingControllerの紹介記事です。'),
  ];

  constructor(
    public navCtrl: NavController,
  ) { }

  onArticleClicked(article: Article) {
    this.navCtrl.push(ShowPageComponent, {
      article: article,
    });
  }
}

onArticleClickedメソッド内で、InjectされたNavControllerpushメソッドを呼び出している。

一方、詳細画面は次のようになっている。



import { Component } from '@angular/core';
import { NavController, NavParams } from 'ionic-angular';
import { Article } from '../../../models/article.model';

@Component({
  selector: 'page-article-show',
  templateUrl: './show.component.html'
})
export class ShowPageComponent {

  article: Article;

  constructor(
    public navCtrl: NavController,
    navParams: NavParams,
  ) {
    this.article = navParams.get('article');
  }
}

NavParamsをInjectして、getメソッドでパラメタを受け取っている。

コンポーネントをNgModuleに登録するときの懸念

このようなディレクトリ構成を取ると、IndexPageComponentのような名前のコンポーネントが大量にできてしまう。NgModuleに登録するときに多少工夫しないと、名前が重複してしまってうまくいかない。

src/app/app.module.tsimportがたくさん並ぶと見づらいので、コンポーネントのリストをsrc/app/components.tsで作ってそれをapp.module.tsimportするようにしてみた。



// Navigations
import { TabsComponent } from '../navigations/tabs/tabs.component';

// Pages
import { IndexPageComponent as ArticleIndexPageComponent } from '../pages/article/index/index.component';
import { ShowPageComponent as ArticleShowPageComponent } from '../pages/article/show/show.component';
import { IndexPageComponent as UserIndexPageComponent } from '../pages/user/index/index.component';
import { ShowPageComponent as UserShowPageComponent } from '../pages/user/show/show.component';

export default [
  TabsComponent,
  ArticleIndexPageComponent,
  ArticleShowPageComponent,
  UserIndexPageComponent,
  UserShowPageComponent,
];



import { NgModule, ErrorHandler } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
import { MyApp } from './app.component';

import components from './components';

@NgModule({
  declarations: [
    MyApp,
    ...components,
  ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp)
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
    ...components,
  ],
  providers: [
    StatusBar,
    SplashScreen,
    {provide: ErrorHandler, useClass: IonicErrorHandler}
  ]
})
export class AppModule {}

まとめ

  • IonicでTabsコンポーネントを使うことでタブ型のナビゲーションを作ることができる。
  • コンポーネントの命名規則とディレクトリの整理ルールを早い段階で作らないと、ArticleListComponentSingleArticleComponentArticleReadUserComponent(例:記事を読んだユーザーリスト)などどんどん無秩序に増えてしまって効率が悪い。
  • 命名規則はRuby on Railsのようにエンティティごとにディレクトリを作り、エンティティに対する主要操作ごとにサブディレクトリを作る方法が一つのソリューションかもしれない。

Code on GitHub