ionic でリッチなモバイルWEBサイトを実現する《AngularJS Advent Calendar 2014》


これは [AngularJS Advent Calendar 2014](http://qiita.com/advent-calendar/2014/angularjs) の２４日目(12/24)の投稿です。クリスマス・イブですね。

ionic は [公式ページ](http://ionicframework.com) で紹介されているとおり、モバイル向けの AngularJS ベースの CSS/JavaScript ライブラリです。（要は Bootstrap 的なものと思ってもらえれば。）

![8cf37330-909a-5bb8-a688-27771838bd3b.png](https://qiita-image-store.s3.amazonaws.com/0/35733/a08e8bbe-ba2c-581e-4344-9954416ede48.png "8cf37330-909a-5bb8-a688-27771838bd3b.png")

[Cordova](http://cordova.apache.org) と連携して、ネィティブアプリにパッケージできます。WebStorm をお使いの方は、バージョン９の更新情報で、ionic がサポートされた、というのをご存知かと思います。

この ionic ですが、アプリにパッケージしなくても、この CSS/JavaScript ライブラリを **モバイル向けWEBサイト** の用途として利用するのもいいんじゃないか、と思っています。もしくはネィティブアプリの一部機能を、ionic で作って、アプリの WebView を通してユーザに提供するとか。WEBなので、iOS/Android で動作しますし。

今回は ionic を利用する手順と、簡単なデモとして Qiita の新着投稿を表示する単純なEBサイトを作ってみたので紹介したいと思います。

> **デモ(モバイル向け)**
> ⇒ **http://hkusu.github.io/ionic-website-demo/dist**
> ※ 手持ちの iPhone 5S 向けにしか最適化していません..

![スクリーンショット 2014-12-22 18.38.17.png](https://qiita-image-store.s3.amazonaws.com/0/35733/053ae42b-5664-4650-2529-f023a2869960.png "スクリーンショット 2014-12-22 18.38.17.png") ![スクリーンショット 2014-12-22 18.56.24.png](https://qiita-image-store.s3.amazonaws.com/0/35733/2432c416-2321-b1de-b13b-670202d91a1f.png "スクリーンショット 2014-12-22 18.56.24.png")

私はデザインや CSS が出来ないので^^; 見た目に関する部分は全て ionic が提供するもので構成しました。画像も一切、利用していません。このあたりが出来ると、もっとリッチな見せ方ができるかと思います。

### 今回の方針

ionic を Cordova からのネィティブアプリとして利用するのではなく、純粋な JavaScript ライブラリとして扱います。その場合、Yeoman のジェネレータとして [Ionic Framework generator(generator-ionic)](https://github.com/diegonetto/generator-ionic) がありますが、プロジェクトによってはこのジェネレータで作成される構成が合わないこともあります。

ionic は Bower で JavaScript ライブラリとして導入することも出来ます（[GitHubはこちら](https://github.com/driftyco/ionic)）ので、今回は [generator-gulp-angular](https://github.com/Swiip/generator-gulp-angular) の構成に Bower で ionic を導入する、という手順でいきます。

> もしサーバサイドの API を Node.js(Express) で用意するなら [AngularJS Full-Stack(generator-angular-fullstack)](https://github.com/DaftMonk/generator-angular-fullstack) の構成が便利です。こちらに書きました。
> ⇒ [Node.js(Express) と MongoDB で RESTful な API サーバを作る](http://qiita.com/hkusu/items/9bb59541c64696b15730)

### 今回の前提環境

私の手元の環境は次のとおりです。

> OS：`Mac OS X 10.9.5`
> Node.js：`0.10.32`
> YEOMAN(yo)：`1.3.2`
> gulp：`3.8.10`
> Bower：`1.3.12`

もし Node.js や Yeoman の環境がなければ、こちらを参考に導入ください。
⇒ [Mac で Yeoman の環境を構築する手順](http://qiita.com/hkusu/items/0d2ed38c47b73201f4c2)
gulp は `$ npm install -g gulp` で導入できます。

また今回の手順で、結果的に導入された主要なモジュールのバージョンは次のとおりです。

> generator-gulp-angular：`0.7.3`
> AngularJS：`1.3.7`
> ionic：`1.0.0-beta.14-nightly-924`
> angular-ui-router：`0.2.13`

IDE は何でもいいですが、ionic やるなら WebStorm がオススメです。バージョン９から ionic の補完が効くようになりました。

![スクリーンショット 2014-12-18 17.37.49.png](https://qiita-image-store.s3.amazonaws.com/0/35733/fdfec746-99f2-ef68-efc1-d655c5227735.png "スクリーンショット 2014-12-18 17.37.49.png")

## ローカル環境の構築

`generator-gulp-angular` がまだ入ってなければ導入します。

```shell:
$ npm install -g generator-gulp-angular
```

適当なディレクトリを作り、ジェネレータを起動します。

```shell:
$ mkdir mobile_demo
$ cd mobile_demo
$ yo gulp-angular
```

いろいろ聞かれるので、自身のプロジェクトに合わせてオプションを選択していってください。（最低限、Bootstrap は ionic と CSS が競合するので外してください。）

参考までに私が今回、選択したオプションは次のとおりです。

![screen5.png](https://qiita-image-store.s3.amazonaws.com/0/35733/dd89b5cc-b4d8-b1f4-eb2c-9d69c4806a03.png "screen5.png")

> ↑ AngularJS は最新バージョンを選択

![screen9.png](https://qiita-image-store.s3.amazonaws.com/0/35733/3495adfc-0ce9-d2d9-4ab6-f1bef471608c.png "screen9.png")

> ↑ リスト表示された AngularJS のサブモジュールは全て導入

![screen3.png](https://qiita-image-store.s3.amazonaws.com/0/35733/1c6c75c0-697e-2bd2-6f09-423e247e68fd.png "screen3.png")

> ↑ jQuery は特に用途がないので導入しない

![screen8.png](https://qiita-image-store.s3.amazonaws.com/0/35733/7c0ebd5d-1cd3-f1a9-cf7d-8cc7c594b183.png "screen8.png")

> ↑ 特に REST をサポートするサブモジュールはいれない

![screen7.png](https://qiita-image-store.s3.amazonaws.com/0/35733/ee08af6c-8462-eb35-750f-8238fc150e06.png "screen7.png")

> ↑ `UI Router` を選択。これは SPA で便利なので、いれておいた方がいいです

![screen1.png](https://qiita-image-store.s3.amazonaws.com/0/35733/c0331283-7cb6-ec44-9456-2f5c1a62f734.png "screen1.png")

> ↑ CSS フレームワークは後で ionic をいれるので、何も選択しない

![screen2.png](https://qiita-image-store.s3.amazonaws.com/0/35733/df56754b-54b3-6898-74ee-22d6de079c4d.png "screen2.png")

> ↑ CSS は生の CSS で書く。でも好みに応じて Sass を選択してもいいかと

ジェネレータでの生成が終わると、次のようにファイルが展開されます。

![screen4.png](https://qiita-image-store.s3.amazonaws.com/0/35733/8fb73ad3-df0b-a9db-90fc-6c7270f92d69.png "screen4.png")

一旦、ここで動作確認します。`$ gulp serve` します。

![screen6.png](https://qiita-image-store.s3.amazonaws.com/0/35733/31048ef7-a492-f284-e450-9a76f44ccfe7.png "screen6.png")

続いて ionic を Bower で導入します。

```shell:
$ bower install driftyco/ionic-bower#master --save
```

> `$ bower install ionic` だと AngularJS のバージョンが合わなかったので、上記のように最新の ionic を導入しました。

次のコマンドにより `index.html` に Bower で導入したライブラリを読み込む記述が自動で追記されます。

```shell:
$ gulp wiredep
```

※ ただ、なぜか私の環境だと CSS が読まれなかったので、次の記述を事前に `index.html` に追記しました。

```html:headエリアに追記
<!-- build:css(client) app/vendor.css -->
<!-- bower:css -->
<!-- endbower -->
<!-- endbuild -->
```

結果的に、追加したモジュールは `index.html` は次のように記載されます。

```html:index.html
〜
<!-- build:css(client) app/vendor.css -->
<!-- bower:css -->
<link rel="stylesheet" href="../bower_components/ionic/css/ionic.css" />
<!-- endbower -->
<!-- endbuild -->
〜
<script src="../bower_components/ionic/js/ionic.js"></script>
<script src="../bower_components/ionic/js/ionic-angular.js"></script>
〜
```

ついでに、モバイル用に `viewport` を修正しておきます。

```html:index.html
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
```

また ionic のアイコンを使いたいので [ionicons](http://ionicons.com/) も導入します。CDN の URL がここで公開されているので、
⇒ http://ionicons.com/#cdn

それを `index.html` に追記します。

```html:index.html
<link rel="stylesheet" href="http://code.ionicframework.com/ionicons/2.0.0/css/ionicons.min.css">
```

> Bower でも ionicons を導入できるのですが、minify するとアイコンが表示されませんでした.. またファイルをコピーしてプロジェクトに配置してもよかったのですが、`wiredep` の仕組みや gulp のタスクを修正する必要があり、今回は説明を簡単にするために CDN を利用することにします。

次に、AngualrJS に ionic を組み込みます。（`ionic` を追記）

```javascript:src/app/index.js
'use strict';

angular.module('mobileDemo', ['ngAnimate', 'ngCookies', 'ngTouch', 'ngSanitize', 'ui.router', 'ionic'])
  .config(function ($stateProvider, $urlRouterProvider) {
    $stateProvider
      .state('home', {
        url: '/',
        templateUrl: 'app/main/main.html',
        controller: 'MainCtrl'
      });

    $urlRouterProvider.otherwise('/');
  })
;
```

既存の `main.css` はデフォルトページ用の CSS が記載されているので、中身を空にします。次に `main.html` を ionic のタグで書いてみます。

```html:src/app/main/main.html
<ion-pane>
  <ion-header-bar class="bar-energized">
    <h1 class="title">タイトル</h1>
  </ion-header-bar>
  <ion-content>
    Hello！
    <i class="ion-social-octocat"></i>
  </ion-content>
</ion-pane>
```

`http://localhost:3000` にアクセスして、このように表示されればOKです。

![スクリーンショット 2014-12-18 17.46.48.png](https://qiita-image-store.s3.amazonaws.com/0/35733/19136ff7-2ec1-426f-3989-2d0a5e37a202.png "スクリーンショット 2014-12-18 17.46.48.png")

以上で導入は完了です^^

## ionic で作る

下記あたりのリファレンスと睨めっこしながら開発をしていきます。ロジックは普通に AngularJS で書きます。（ぶっちゃけ ionic の CSS だけ利用するなら、AngularJS でなくてもたぶんいけると思う。）

- http://ionicframework.com/docs/api/
- http://ionicframework.com/docs/components/
- http://ionicframework.com/html5-input-types/
- http://ionicons.com/

冒頭のデモの場合だと、ソース構成はこんな感じです。

![スクリーンショット 2014-12-22 19.08.27.png](https://qiita-image-store.s3.amazonaws.com/0/35733/38e353f4-e805-2920-d726-ae3ec8f25dbe.png "スクリーンショット 2014-12-22 19.08.27.png")

> ※ **ソースは GitHub に置きました。必要に応じて参照ください。**
⇒ **https://github.com/hkusu/ionic-website-demo**

ルーティングには [AngularUI Router](https://github.com/angular-ui/ui-router) を使っていて、state(状態)に対して、Controller と View を紐付けています。

```javascript:src/app/index.js
〜
$stateProvider
  .state('main', {
    url: '/main',
    templateUrl: 'app/main/main.html',
    controller: 'MainCtrl'
  })
  .state('main.top', {
    url: '/top',
    views: {
      'menuContent': {
        templateUrl: 'app/top/top.html',
        controller: 'TopCtrl'
      }
    }
  })
  .state('main.qiita', {
    url: '/qiita',
    views: {
      'menuContent': {
        templateUrl: 'app/qiita/qiita.html',
        controller: 'QiitaCtrl'
      }
    }
  })
〜
```

SPA(シングルページアプリケーション)のページを切り替えるタグはこんな感じ。上記の state(状態) を切り替えます。リンク時に引数も渡そうと思えば渡せます。

```html:例
<a ui-sref="main.qiita">
```

Qiita のデータ取得には [Qiita API (v1)](https://qiita.com/api/v1/docs) を利用してます。次のようにたった３行です。

```javascript:src/app/qiita/qiita.controller.js
$http.get('https://qiita.com/api/v1/items?per_page=30').success(function(items) {
  $scope.items = items;
});
```

> 前に v2 の API を確認したところ、クロスドメイン制約でひっかかる感じだった。いまは違うかもしれない。

あとポイントとしては、Bootstrap臭 ではないのですが ionic 臭が出ないようにすることでしょうか。ionic デフォルトのナビゲーションバーを隠したり、`class="row"` `class="col"` でページの構成をし直したり、背景色を変えたりしています。`class="button-bar"` を使うと、大きいアイコンボタンが作れたりします。モーダル表示は一箇所でしか使ってない（チュートリアルのところ）のですが、なんとなくモーダルはスマホと相性がいいのうな気がするので、うまく使うと UX が高まるかと思います。

リリース用にビルドする場合は `$ gulp build` とすると `dist` フォルダにデプロイ用のファイルが展開されます。

## おわりに

今回のものはデモ用途なので多くの機能はありませんが、WEB でも SPA構成にする ＆ ionic のようなライブラリを利用することで、それなりのものが実現できるかと。もちろん UI/UX はネィティブアプリに劣りますが、簡単なものなら WEB で実現できるし、ネィティブアプリの形式が良ければ Cordova を利用してパッケージするか、部分的にアプリの WebView から表示する、というのも手かなあと思います。もしくは静的なWEBサイトを、この構成にしてもいいですね。

また、下記は ionic でなく [Oonsen UI](http://ja.onsen.io) を利用して作ってみたデモです。よろしければこちらもご覧ください。（手順は、もし需要があったら後日、書きたいと思います。）
⇒ **http://hkusu.github.io/gp-kao-catalog** (こちらもモバイル向け)

![スクリーンショット 2014-12-24 11.48.43.png](https://qiita-image-store.s3.amazonaws.com/0/35733/f2e75d38-e5f2-fc3f-3e9e-5ad47b3abe5b.png "スクリーンショット 2014-12-24 11.48.43.png") ![スクリーンショット 2014-12-24 11.49.03.png](https://qiita-image-store.s3.amazonaws.com/0/35733/7316ab57-3fd9-c2a7-7d81-9818bf098227.png "スクリーンショット 2014-12-24 11.49.03.png")

では良いクリスマスを^^