[riot 習作]riotでAPIテストツールを作ってみよう

皆さん、こんにちは

最近のJavaScriptのフロントサイドのフレームワークもしくはアーキテクチャは、AngularとReactJSが主流となっているように感じますが、一方で、vue.jsやriot.jsのような大企業がバックにないようなフレームワークにも光が当たってきています。
個人的にはどれがいいというよりは、作成するアプリの性質に応じて使い分けるのがいいんじゃないかなぁと思いますが、弊社内ではAngular, ReactJS, vue.jsを推す人はいるのですが、riot.jsを推す人がいなかったので、じゃあ私がやっちゃおうということでやりました。

TODOアプリとかはすでに公式サイトにあるので、ここではAPIをテストするためのツール作成を通して、riot.jsの挙動を調べてみましょう。

ちなみに今回のはriot 3.0を使っているのでそれ以前のバージョンを使っている場合は、動かない部分があります。
まだ日本語訳されていないので、ちょいと面倒かも。。。

# 概観
## 仕様

今回作成したのは、特定のURLにリクエストを投げ、レスポンスをテキストフィールドに書き出すというものです。
UIは以下のとおりです。

<img width="765" alt="スクリーンショット 2016-12-31 10.04.38.png" src="https://qiita-image-store.s3.amazonaws.com/0/102182/eae465be-7cec-ff86-96b5-4f6c03671da3.png">

## riot

riotは「軽量」「シンプル」「コンポーネント」をテーマに作られたフレームワークです。
http://riotjs.com/
http://riotjs.com/v2/ja/

カスタムタグという、オリジナルのタグを作る仕組みを通して、UIとそれに伴う処理をまとめておくことができます。
私のようにモノグサな人間にも、割と簡単に書くことができます。

## 検証内容
riotの以下の機能について検証しています

- タグの作成
- 入れ子のタグ
- 描画タイミング

# 実装
## 大本のHTML
riotを動かすにもまずはエントリーポイントとなるHTMLを作らなければなりません。
HTMLは以下のとおりです。

```index.html
<html>
<head>
  <title>API TEST</title>
  <!-- Latest compiled and minified CSS -->
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<div class="container">
  <h1>API TEST</h1>

  <!-- Main Content tag -->
  <test-api></test-api>

  <script src="https://code.jquery.com/jquery-3.1.1.min.js" integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=" crossorigin="anonymous"></script>
  <!-- include the tag -->
  <script type="riot/tag" src="tags/test-api.tag"></script>
  <script type="riot/tag" src="tags/test-uri.tag"></script>
  <script type="riot/tag" src="tags/test-input.tag"></script>
  <!-- include riot.js -->
  <script src="https://cdn.jsdelivr.net/riot/3.0/riot+compiler.min.js"></script>
  <!-- mount the tag -->
  <script>riot.mount('*')</script>
</div>
</body>
</html>
```
bootstrapは見た目をちょっと良くするために導入し、jqueryはajaxを容易に使いたいので入れています。
このHTMLのなかでメインとなるのは`test-api`タグで、この中にほぼすべての処理が入っています。

riotで使用できるオリジナルタグの定義は`tags`ディレクトリに入れてあります。
最後の`riot.mount('*')`で読み込んだタグを全てマウントしています。
## 子タグの作成
メインのタグである`test-api`はサイズが大きいので、先にサイズの小さい`test-uri`, `test-input`を先に実装してみましょう。

```html:tags/test-uri.tag
<test-uri>

<div class="form-inline">
<div class="form-group">
    <label for="uri">URI: </label>
    <input class="uri form-control" type="text" style="width: 600px" ref="uri"></input>
</div>
</div>

</test-uri>
```
`tag`という見慣れない拡張子を使っていますが、これがriotで使用されるカスタムタグを定義するためのファイルになります。
カスタムタグはそのタグ名で囲った範囲を新たなタグの定義として認識します。
ここでは何か特別な処理があるわけではないので、見た目は通常のHTMLとほとんど同じです。
唯一`ref`という属性が定義されていますが、これは後で使います。

同様に`test-input`タグも定義しましょう。

```html:tags/test-input.tag
<test-input>

field: <input type="text" ref="field"></input>
value: <input type="text" ref="value"></input>

</test-input>
```
丸裸の`input`二つだけの構成となりました。
わざわざカスタムタグを定義する必要もなさそうですが、なんとなく外に出しています。

## メインのタグを定義する
### UIの定義
今回のAPIテストツールのメインとなるタグを作成しましょう。
まず、UIの部分だけ作ってみます。

```html:tags/test-api.tag
<test-api>

<!-- tags/test-uri.tag で作ったタグ -->
<test-uri></test-uri>

<br>

<div class="form-group">
    <label for="method">method: </label>
<select ref="method">
    <option each={ method in methods } value={ method }>
        { method }
    </option>
</select>
</div>

<div>
    <h2>Field and Value</h2>
    <button onclick={ addItem } >add</button>
    <ul>
        <li each={ value, key in reqItems }>
            <!-- tags/test-input.tag で作ったタグ -->
            <test-input></test-input>
            <button onclick={ deleteItem } >remove</button>
        </li>
    </ul>
</div>

<button onclick={ showResult } >result</button>

<br>

<textarea class="form-control" rows="20">{ result }</textarea>

<!-- 後略 -->
</test-api>
```
ここではいくつか特殊な記法が出てきます。
まず、後述するタグのプロパティに対しては中括弧でアクセスできます(`this.item`にアクセス => `{ item }`)。
optionやliタグについている`each`属性は配列の各要素を使って、そのタグを生成します。
例えば、methodの`option`の部分は実際には以下のように展開されています

```html:
<option value="GET"> GET </option>
<option value="POST"> POST </option>
<option value="PUT"> PUT </option>
<option value="DELETE"> DELETE </option>
```
### 処理の作成
次に、プロパティの定義と処理の部分を作成します。
この部分はカスタムタグの中で`script`タグで定義すれば良いです。

```html:tags/test-api.tag
<test-api>
<!-- 中略 -->
<script>
this.methods = ['GET', 'POST', 'PUT', 'DELETE'];
this.reqItems = [];
this.result = '';
// 中略
</script>
</test-api>
```
まずはリクエストパラメータを追加・削除する処理を作ってみましょう。
UIの部分で、パラメータの追加はaddボタンの`onclick`で起動する`addItem`メソッドで、パラメータの削除は各removeボタンの`removeItem`になります。

```js:tags/test-api.tag
addItem() {
    this.reqItems.push({field: '', value: ''})
}

deleteItem(e) {
    let key = e.item.key
    this.reqItems = this.reqItems.filter((v, i) => i != key)
}
```
メソッドの書き方はこんな感じでできます。
`addItem`は`reqItems`に要素を追加し、`deleteItem`は押したボタンが属する`li`タグに紐付いたオブジェクトを削除しています。
これだけで動きます。妙に簡単ですね。

### 通信処理の作成
次に、リクエスト処理の部分を書いてみましょう。
まずは`result`ボタンを押したときに起動するメソッドを作ってみましょう。

```js:tags/test-api.tag
showResult() {
    let uri = this.tags['test-uri'].refs.uri.value

    if (typeof this.tags['test-input'] == 'undefined') return this.query(uri, [])

    let inputs = this.tags['test-input']
    if (typeof inputs[0] == 'undefined') inputs = [inputs]

    this.query(uri, inputs)
}
```
入れ子のタグ要素は上述したように`this.tags`に格納されています。
また、各カスタムタグの中で`ref`属性が付加されたDOMに、`this.refs['refname']`でアクセスすることができます。
今回注意すべきは**このrefsによるアクセスはversion 3からの追加**であることです。
version 2では使えないこと、およびversion 2でできていたname,idへの要素への直接アクセスができなくなったことに注意しておきましょう。


最後に通信の部分を見てみましょう。

```js:tags/test-api.tag
query(uri, inputs) {
    const reqParam = {}
    inputs.forEach(v => {
        let field = v.refs.field.value
        let value = v.refs.value.value
        reqParam[field] = value
    })
    $.ajax({
        type: this.refs.method.value,
        url: uri,crossDomain: true,
        data: reqParam
    }).done(res => this.renderResult(res))

}

renderResult(data) {
    this.result = JSON.stringify(data, null, "\t")
    this.update()
}
```
jQueryのajax使っているのは単純にソッチのほうが慣れているからです。
最後の部分ですが、`this.result`に値を入れただけでは表示が更新されないようで、`this.update()`メソッドで、明示的にレンダリングさせています。
一方、eachで監視されているプロパティの変更においてはupdateをかけなくても表示が更新される模様です。

# サーバサイドの実装
本題と外れますが、サーバサイドはKoaを使っています

```shell-session
npm i -S koa koa-router koa-body
```
で必要なモジュールをインストールしてから

```server.js
"use strict";

const koa = require('koa');
const app = koa();
const router = require('koa-router')();
const koaBody = require('koa-body')()

router
  .get('/app', function * (){
      queryParam(this, this.query)
  })
  .post('/app', koaBody, function * (next){
      queryParam(this, this.request.body)
  })

function queryParam(ctx, params) {
    let query = params
    let url   = ctx.url

    ctx.response.set("Content-type", "application/json; charset=UTF-8")
    let body = {uri: url, param: query}
    console.log(body)
    ctx.body = JSON.stringify(body)
}

app.use(router.routes())

app.use(require('koa-static-server')({rootDir: '', rootPath: '/client'}));

app.listen(8888);
```
こんな感じで、非常に単純に実装しちゃってます。

# まとめ

HTML書くのは慣れているんですが、JavaScriptでDOMを制御しようとすると、非常に面倒なんですよね。
そんなわけで、フロントのUIを簡単に制御できるJSのフレームワークのなかで、riotを使ってAPIテストツールを作ってみました。
はじめは動きがわからない部分もありましたが、半日程度で動きは把握できました。
謳い文句にあるだけあって、学習コストは非常に小さいと思います(とはいえ、HTMLとJSの知識前提ですが)。

今回はこんなところです。

#参考
[Riot.js 2.0 を触ってみた — まだReactで消耗しているの?](http://qiita.com/cognitom/items/fb1295f3f93911e9e92d)