monacaでTabとNavigationを組み合わせたテンプレートを作った

monacaでTabによるページ切り替えとNavigationによるプッシュ/ポップの2つの画面遷移を組み合わせたサンプルアプリを作成してみました。

一通りの画面遷移をしているデモ動画です。
![ios.gif](https://qiita-image-store.s3.amazonaws.com/0/44992/4d444118-9c24-91d4-8d73-ced90f38afbc.gif)
AndroidでもAndroidの見た目になりつつ同様の機能が実現できています。
![android.png](https://qiita-image-store.s3.amazonaws.com/0/44992/181077a0-a5d9-bdb5-ce75-04e0e7e49be6.png)

テンプレートはGitHubで公開中です。
https://github.com/kuluna/monaca-template-tab-navigation
利用方法はREADME.mdを参照してください。
monacaのフリープランで実際に動かすことができます。

また、作成したテンプレートは画面遷移の他にmonacaでアプリを作成する上でよく使う(と個人的に思っている)機能もサンプルとして実装していますので、よろしければ参考にしてください。

- タブとナビゲーションの組み合わせ
- Push Navigation時の値渡し
- AngularJSのhttp APIを使ったREST API通信
- Nifty Mobile Backendモジュールの組み込み

# 読むと幸せになれるかもしれない人
- monaca チョットワカル人
- AngularJS チョットワカル人
- スマートフォンアプリ ツクリタイ人

つまり↓のような人ですね。
![ipsj_07.png](https://qiita-image-store.s3.amazonaws.com/0/44992/0cd20c8e-f5b9-3f1d-4ec3-755785b71498.png)

# タブとナビゲーションの組み合わせ
このテンプレートの一番の特徴がTabとNavigation両方の画面遷移ができることです。

画面のヒエラルキー

```
index.html(Tab)
│
├─view/home.html(Navigation)
│  ├─master.html
│  ├─detail.html
│
└─view/account.html
```

```index.html
<ons-page ng-controller="AppController">
  <!-- Tab is root navigator. -->
  <ons-tabbar position="bottom">
    <ons-tab page="view/home.html" label="Home" icon="home" active></ons-tab>
    <ons-tab page="view/account.html" label="Account" icon="user"></ons-tab>
  </ons-tabbar>
</ons-page>
```

画面の切り替えとしてタブが一番上です。タブの中の1ページ(このテンプレートではhome)にNavigationがあり、homeの中でmasterとdetailが遷移します。なので、masterからdetailに遷移しても下のタブは消えません。

```view/home.html
<ons-page>
  <!-- Javascript can use named by home.nav -->
  <ons-navigator var="home.nav" page="view/home/master.html"></ons-navigator>
</ons-page>
```

home.htmlはNavigationによる画面遷移だけを担当し、さらに子画面でUIを実装していく感じです。

```view/home/master.html
<ons-page ng-controller="HomeMasterController">
  <ons-toolbar>
    <div class="left"><ons-back-button>Back</ons-back-button></div>
    <div class="center">Master</div>
  </ons-toolbar>

  <div class="content">
    <h3>Choose the number.</h3>
    <ons-list>
      <ons-list-header>Numbers</ons-list-header>
      <ons-list-item ng-repeat="n in numbers" ng-click="showDetail(n)" tappable>{{n}}</ons-list-item>
    </ons-list>
  </div>
</ons-page>
```

画面上に表示されるToolbarですが、このように子画面に実装したほうが良いです。画面毎に表示したいタイトルやボタンを設定することができます。
子画面毎にAngularのControllerを指定できるので、ここでもAngular式が使えます。

# Push Navigation時の値渡し
Push時に値を渡すことで、遷移先でその値を使った処理を行うことができます。
やり方なのですが、実は日本語ドキュメントが古いのでここで引っかかってしまう人は多いのではないでしょうか。
app.jsでは`HomeMasterController`にある`showDetail`でPushをしています。

```app.js
ons.bootstrap()
    .
    .
    .
  .controller('HomeMasterController', function($scope) {
    .
    .
    .
    // Define Event function
    $scope.showDetail = function(number) {
      // Push page with params
      home.nav.pushPage('view/home/detail.html', { data: { num: number } });
    };
    .
    .
    .
```

説明の前に、`home.nav` **はどこから出てきたん？**と思うでしょう。
この変数名はNavigationタグが書いてある`view/home.html`で定義されています。

```view/home.html
<ons-page>
  <!-- Javascript can use named by home.nav -->
  <ons-navigator var="home.nav" page="view/home/master.html"></ons-navigator>
</ons-page>
```

`view/home.html`にある`<ons-navigation>`は`home.nav`という変数名で使えるよ、ということです。

ではpushPage関数の説明に戻って、第2引数で遷移先に渡す値を指定しています。
ひっかかるのはここで、第2引数のJSONは**dataというkeyの中に値を入れないと動きません。**

ただし、これさえできれば渡し方も受け取り方も簡単です。
受け取る際は受け取り先のControllerで以下のようにすればOkです。

```app.js
ons.bootstrap()
.
.
.
  .controller('HomeDetailController', function($scope) {
    // Get the params from pushed page
    const extra = home.nav.topPage.data;
    $scope.num = extra.num;
    .
    .
    .
  })
.
.
.
```

`home.nav`の`topPage`というkeyがdataを持っています。
後はこれを煮るなり焼くなりすればOKです。

# AngularJSのhttp APIを使ったREST API通信
Accountページで利用しています。
AngularJSを触ったことのある人はすぐにわかると思います。
GitHub上にあるOnsenUIのユーザー情報を取得するAPIをGETで取得し、$scopeの中に代入しています。

```app.js
ons.bootstrap()
.
.
.
  .controller('AccountController', function($scope, $http) {
    // Get the OnsenUI GitHub
    $http.get('https://api.github.com/users/OnsenUI').then(function(response) {
      if (response.status === 200) {
        $scope.onsenui = response.data;
      } else {
        alert('Error http: ' + response.status);
      }
    });
  });
```

AngularJSではおなじみの$httpをDIしています。
POST、DELETE等は公式ドキュメントが参考になります。
https://docs.angularjs.org/api/ng/service/$http

HTMLはonsenuiの値を取り出して表示しているだけです。
日付はJSONのデータそのままではわかりにくいため、AngularJSのフィルタという機能を使ってフォーマットを行っています。
https://docs.angularjs.org/api/ng/service/$filter

```view/account.html
<ons-list-item>Last Update: {{onsenui.updated_at | date: 'yyyy/MM/dd HH:mm:ss'}}</ons-list-item>
```

`|`の後ろにフィルタ名を指定することでAngularJSのフィルタ機能が使えます。dateフィルタの場合は続く文字列でフォーマットを指定します。

# Nifty Mobile Backendモジュールの組み込み
このテンプレート内でNifty Modile Backendを使ったプッシュ通知を受け取るためのmonaca側で必要な事前のセットアップがされています。
[プッシュ通知（Monaca）- Nifty Cloud](http://mb.cloud.nifty.com/doc/current/push/basic_usage_monaca.html)

また、サンプル実装としてプッシュ通知を受け取るためのコードがapp.js内に書かれています。
が、Nifty側のセットアップをしないと動かないため、テンプレートではコメントアウトした状態にしています。

```app.js
// Cordova ready
ons.ready(function() {
  // Nifty Mobile Backend(Push notification)
  window.NCMB.monaca.setDeviceToken(
    "", // Nifty application key
    "", // Nifty client key
    ""  // Firebase ID
  );
  window.NCMB.monaca.setReceiptStatus(true);
});
```

ons.ready内はcordvaプラグインが利用可能な状態になっていますので、ここでNifty Cloudプラグインをセットアップしています。
空欄にそれぞれのKeyとIDを設定すればmonaca側はOKです。

> プッシュ通知はmonacaデバッガー上では動きません。monacaでアプリをビルドしスマートフォンにインストールして初めて動作します。

## 参考: monacaのプッシュ通知
monacaにも標準でプッシュ通知の仕組みが備わっているのですが、私が試してみたところAndroidでプッシュ通知受信時に音もバイブレーションも鳴りませんでした。
Nifty Cloudは受信時にきちんと通知音を鳴らしてくれますし、CordovaのVibrationプラグインを有効にしておくことでバイブも鳴りました。

また、料金プランを見ても無料の範囲でプッシュ通知をたくさん送れるNifty Cloudのほうが良いと思います。
monacaではmonaca画面上からしかプッシュ配信できませんが、Nifty Cloudなら無料枠で↓の参考のようにプログラムからプッシュ配信ができます。

## 参考: サーバーからのプッシュ配信
Node.jsでプッシュ通知を送るためのサンプルソースを作りました。
https://gist.github.com/kuluna/fa5d48e1cc4ccb59a2f93553639d512c

## いらない、消したい
monacaの設定ダッシュボードからNiftyプラグインを無効にすることでオプトアウトできます。
後はapp.js内のons.ready内に書かれている`window.NCMB`から始まるコードを削除すればOKです。