Quasarでココイチのトッピング選択アプリを作ってみた

週に1度はココイチに行くのですが、毎回似たようなメニューを選んでしまいがちです。
そこでトッピングの組み合わせをランダムで表示してくれるWebアプリ、
「**[CoCo Helper](https://coco-helper.netlify.app)**」を製作しました。


# CoCo Helperについて
## 使い方
### 1. 条件設定画面
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/158453/03ff08f8-a8df-2925-b243-2c4746155059.png" width="360" />
1. 金額の範囲
2. ベース（カレーソースの種類）
3. ライスの量
4. 辛さ
5. カレーソースを増量するか

以上を指定して、「絞り込む」ボタンを押すと次の画面に遷移します。

### 2. 検索結果画面
条件にマッチしたトッピングの組み合わせが表示されます。
<img src="https://qiita-image-store.s3.amazonaws.com/0/158453/ad3db42e-a77c-5962-a161-401a882d72a4.png" width="360" />
検索結果はトッピングの数でさらに絞り込んだり、並び順の変更ができます。

## 今後追加したい機能
トッピング1品を固定できる機能があってもいいかなと思っています。

> 2017年12月17日 追記
> この機能を実装しました。
> カテゴリー（肉類/魚介類など）だけの固定もできるようにしてあります。


# 使用したツールやサービス
ここからは技術的な内容について書いていきます。

## Quasar
Vue.jsベースのWebアプリ開発フレームワークです。
http://quasar-framework.org/

### 導入したきっかけ
今までにVue.jsを使用していくつかミニアプリを作ってきましたが、
CSSをゼロから書くのは結構面倒なんですよね。
BootStrapなどのCSSフレームワークを使用することも考えましたが、
Vue.jsのUIライブラリは今までに使用したことがなかったので
試してみることにしました。

[10 Best Vue.js based UI Frameworks for Building Mobile Apps](https://superdevresources.com/vuejs-mobile-frameworks/)
[25+ Best Vue.js Frameworks](https://cssauthor.com/vuejs-frameworks/)

上記サイトを参考に比較検討した結果、Quasarを採用しました。
デモサイトがしっかりしていてドキュメントが豊富な点が決め手になりました。

### 導入方法
Vue.jsのCLIとほぼ同様だったので、迷うことなく開発できました。

``` sh:グローバルにCLIをインストール
$ npm install -g quasar-cli
```

``` sh:プロジェクト作成
$ quasar init <folder name>
$ cd <folder_name>
$ npm install
```

これだけで、ひな形のディレクトリ構造とファイルが作成され、
開発に必要なWebpackやESLintなどのパッケージがインストールされます。

``` sh:開発時のコマンド
$ quasar dev
```
デフォルトブラウザが立ち上がり、製作中のWebアプリが表示されます。
ソースコードを保存すると、連動してブラウザが再描画されたりします。

``` sh:書き出しのコマンド
$ quasar build
```
ソースコードはそのままではブラウザで実行できないのでビルドが必要です。
このコマンドで `/dist` ディレクトリにHTML, CSS, JSファイルなどが書き出されます。

今回はGitHub Pagesで公開するために、`/docs` ディレクトリに書き出されるよう、
設定ファイルやビルドのスクリプトを修正してあります。[^201910]

### 使用感
#### UIコンポーネントが便利
パーツの見た目や振る舞いについてのコードを書かなくて済むので、
ひたすらロジック部分に集中できるのがいいですね。

たとえば「ライスの量」のスライダーは、このHTMLだけで実装できます。

```html
<q-slider snap markers v-model="riceAmount" :min="200" :max="700" :step="100" />
```

今回は初めてだったので、頻繁にドキュメントを見ながらの作業でしたが、
慣れていけばサクサク開発できそうです。

#### CSSプリプロセッサはStylus
SassでもLESSでもなくStylusなのが珍しいと思いました。
今回Stylusを触ったのはテーマカラーの変数程度で、
他は通常のCSSで済ませています。

#### ESLintのルールがStandard
https://standardjs.com/
普段はAirbnbルールを使用しているので、慣れているものに変更しようかと思いましたが、
新しい気づきがあればと思い、そのまま使用してみました。

しかし、Standardはカンマやセミコロンなどを省略するルールなので、
いつも通りに書いていると、ついセミコロンなどを打ってしまい、
アラートが表示されて、元の位置まで戻って消す、ということが多々ありました。

公式のサイトにあるように、慣れればセミコロンなしは快適なのかもしれませんが、
文法的に省略できない場合もあるので、「セミコロンは必ずつける」ルールと比べると
学習コストは少し高いかなと思いました。

#### ビルド等は若干遅め？
たったこれだけの規模なのですが、開発モードの始動に約10秒、
ビルドに20秒強ほどかかります。
別のVue.jsのCLIで製作したシンプルなアプリの場合、
それぞれ5秒と10秒くらいなので、だいぶ遅く感じました。

UIコンポーネントだけでなく色々な機能があるので、
その分遅くなってしまうのは仕方ないのかなと思います。

#### まとめ
導入の手軽さやコンポーネントの便利さは良かったのですが、
StylusやESLintルールなど、慣れない部分もありました。
他にもUIライブラリはたくさんあるので、いろいろ試してみて
自分に一番合うものを見つけたいと思います。

## Airtable
オンラインのDBサービスです。
サイト上でデータの編集ができるだけでなく、Web APIでデータの取得や操作も可能です。
https://airtable.com/

### 導入したきっかけ
もともと、別のWebアプリの構想を練っているときに見つけたサービスです。
（しかし、そちらは手つかずのまま放置されています）

開発当初、トッピングなどのデータはJSファイルに格納しようと思いましたが、
メニュー変更のたびにコミットしないといけなくなるし、
そもそもJSON形式でテーブルデータを作成・修正していくのは
面倒になりそうだと思いました。

データ形式をCSVファイルにすればデータ加工の手間は減りますが
「都度コミット問題」は残ってしまいます。

そんな時、このAirtableのことを思い出したので、使ってみることにしました。
ベース（カレーソース）のテーブルとトッピングのテーブルを作りました。

<img src="https://qiita-image-store.s3.amazonaws.com/0/158453/2a62e77f-486a-b979-3000-3bdcc1ba9905.png" width="600" />


### 使用感
#### カラムの設定が多彩
文字列、数値、チェックボックスなど「型」が20種類以上から選べます。
UIも直観的なので、あっという間にテーブルのフォーマットが完成しました。

#### クリップボードからの貼り付けが可能
開発に取りかかる前の企画段階で、トッピングデータが必要なのは分かっていたので
ココイチサイトのメニューページにあるトッピングの表をコピーして、
Excelに貼り付け、整形・加工しておきました。

ダメもとでそれをAirtableのテーブルにペーストしてみたところ、
ちゃんとレコードが作成され、軽く感動を覚えました。
ちなみにGoogleスプレッドシートからのインポートもできるようです。

#### APIドキュメントが充実
各コマンドやオプションについての説明があるだけでなく、
実際のテーブルデータでのレスポンスが表示されているのが親切だと感じました。

#### まとめ
今回はデータ置き場として利用しましたが、他にもいろいろな使い方ができそうです。
これだけのサービスが無料で使えるのはすごいですね。
（有料プランもいくつかあり、データ容量が増えたり
　履歴を保管しておける期間が延ばせるようです）

## axios
定番のAjaxライブラリです。
https://github.com/axios/axios

### 導入したきっかけ
jQueryのAjax系メソッドしか使用したことがないので、一度試してみたかったのです。

### 使用感
慣れ親しんでいる非同期の書き方だったので、
ドキュメントを軽く読んだだけて実装できました。

今回はGETメソッドを2つ使っただけなので、
良い点や悪い点についてはあまり分かりませんでした。
今後Ajax処理が必要になった際、また使ってみようと思います。

# メインロジック
トッピングのデータを取得後、あらかじめ全通りの組み合わせを作成しておいて、
条件に合うものだけに絞った後、シャッフルして表示しています。

## 組み合わせを作る
作りたいのは、数学的な「組み合わせ」とは異なるのですが、
まず「順列 組み合わせ プログラミング」で検索して、いくつかのサイトを見てみました。
しかし、やたら複雑だったり、変数の数が多いコードばかりでした。

「いや、もっとシンプルにできるだろう」ということで
結局自分で作ることにしました。

そしてできたのがこちら。

```JavaScript
const createCombinations = (arr, maxCount) => {
  const ret = [];

  (function innerFn(currentIndex = 0, count = 1, passedArr = []) {
    for (let i = currentIndex, len = arr.length; i < len; i++) {
      const tmpArr = passedArr.concat(arr[i]);

      ret.push(tmpArr);

      if (count < maxCount) {
        innerFn(i + 1, count + 1, tmpArr);
      }
    }
  }());

  return ret;
};
```
元となる配列と「何個までの組み合わせを作るか」を引数で渡すと、
組み合わせを持った配列を返します。

途中まで、内側の即時関数`innerFn`はなく、外側の関数に引数を5つ持たせた状態で
再帰処理をしていましたが、役割を分離したらだいぶすっきりしました。

`innerFn`の3行目で`push`している部分を`if (count === maxCount) {`で囲めば、
数学の「組み合わせ」になりますね。

```JavaScript
createCombinations([1, 2, 3, 4], 3);

// そのままだと、1個～3個の組み合わせを返す
[
  [1],
  [1, 2],
  [1, 2, 3],
  [1, 2, 4],
  [1, 3],
  [1, 3, 4],
  [1, 4],
  [2],
  [2, 3],
  [2, 3, 4],
  [2, 4],
  [3],
  [3, 4],
  [4]
]

// if文を追加すると、3個の組み合わせだけになる
[
  [1, 2, 3],
  [1, 2, 4],
  [1, 3, 4],
  [2, 3, 4]
]
```

## 配列のシャッフル
JavaScriptの配列にはシャッフルするためのメソッドがないので、
自分で作らないといけません。

検索すると`Fisher–Yates shuffle`というアルゴリズムが有名なようです。
[Fisher–Yates shuffle](https://bost.ocks.org/mike/shuffle/)
ロジックはそのままで、少し書き換えて実装しました。

```JavaScript
for (let i = arr.length; i--;) {
  const r = Math.floor(Math.random() * i);
  [arr[i], arr[r]] = [arr[r], arr[i]];
}
```
ES2015（モダンなJavaScript）だと値の入れ替えが1行で済みますね。

# おわりに
新しいことをいろいろ試してみるのは面白いですね。
UIコンポーネントの便利さとAirtableの可能性が分かったのは大きな収穫でした。
あと、ポークカレーの値段が地域によって違うのも初めて知りました。

さて、次回はどのトッピングにしようかな…

[^201910]: 2019年10月、Netlify に移行しました