Vue.jsでAPI通信

# Index
[1.VueCLIでHelloWorld](http://qiita.com/MariMurotani/items/5fbea5942d2edf149989)
[2.Vue.jsでフォームを使おう](http://qiita.com/MariMurotani/items/10702fbcae2997fcae80)
[3.Vue.jsで単一ファイルコンポーネント](http://qiita.com/MariMurotani/items/8694bce9e29e1593a488)
[4.Vue.jsでAPI通信](http://qiita.com/MariMurotani/items/a8576974deeabf9daf6e)
[5.Vue.jsで猫検索アプリ作成](http://qiita.com/MariMurotani/items/86a4b267489df3b83c61)
[6.おまけ](https://qiita.com/MariMurotani/items/3496e461e8f4f5545ff2)

-----


# 1. 前提条件
[前回の実習を終わらせておくこと](http://qiita.com/MariMurotani/items/8694bce9e29e1593a488)

# 2.[axios](https://github.com/mzabriskie/axios)と[mockey](https://github.com/2do2go/mocky)をインストール
VueでAjaxするには何を使えばいいの？っていう話が調べていくときな臭い感じ・・・・・
https://jp.vuejs.org/2016/11/03/retiring-vue-resource/
とりあえず、上記の公式コミュニティでおすすめされてる、axiosを使ってみたいと思います。

```sh
npm install axios --save
npm install -D mocky --save
```

# 3. mockeyのインストールとAPIスタブ作成
## 3.1 スタブAPI作成 
```mock.js
var mocky = require('mocky');

mocky.createServer([{
// simple GET route without request body to match
  url: '/profile',
  method: 'get',
  headers: {'Content-type': 'text/json'},
  res: {
    status: 200,
    headers: {'Content-type': 'text/html', 'Access-Control-Allow-Origin': 'http://localhost:8080'},
    body: JSON.stringify({'nickname': ''})
  }
},
{
// simple GET route without request body to match
  url: '/profile_wait',
  method: 'get',
  headers: {'Content-type': 'text/json'},
  res: function(req, res, callback) {
    setTimeout(function() {
      callback(null, {
        status: 200,
        headers: {'Content-type': 'text/html', 'Access-Control-Allow-Origin': 'http://localhost:8080'},
        body: JSON.stringify({'nickname': ''})
      });
    }, 1000);
  }
},
{
// POST route with request body to match and respose with status, headers and body
  url: '/profile',
  method: 'post',
  req: '',
  headers: {'Content-type': 'text/json'},
  res: {
    status: 200,
    headers: {'Content-type': 'text/html', 'Access-Control-Allow-Origin': 'http://localhost:8080'},
    body: JSON.stringify({'status': 'ok'})
  }
},
{
// PUT route with dynamic response body
  url: '/profile',
  method: 'put',
  req: '',
  headers: {'Content-type': 'text/json'},
  res: {
    status: 200,
    headers: {'Content-type': 'text/html', 'Access-Control-Allow-Origin': 'http://localhost:8080'},
    body: JSON.stringify({'status': 'ok'})
  }
},
{
// DELETE route with dynamic response body
  url: '/profile',
  method: 'delete',
  req: '',
  headers: {'Content-type': 'text/json'},
  res: {
    status: 200,
    headers: {'Content-type': 'text/html', 'Access-Control-Allow-Origin': 'http://localhost:8080'},
    body: JSON.stringify({'status': 'ok'})
  }
}]).listen(4321);
```

## 3.2 起動 
```
node mock.js
```
## 3.3 動作確認
```shell
# GET
curl "http://127.0.0.1:4321/profile"
# POST
curl -d "{'nickname': 'test'}" http://127.0.0.1:4321/profile
# PUT
curl -v -X PUT -d "{'nickname': 'test'}" http://127.0.0.1:4321/profile
# DELETE
curl -v -X DELETE -d "{'nickname': 'test'}" http://127.0.0.1:4321/profile
```

# 4. axiosを使ってAPI通信
下記の記述をScene1.vueに追加してAPIデータ取得が非同期である事を確認してみましょう。profile_waitを読んでいるので少し遅れてコンソールにレスポンスが出力されます。

```js:components/Scene1.vue
.......
import axios from 'axios'

export default {
  created: function () {
    axios.get('http://127.0.0.1:4321/profile_wait')
      .then(response => {
        console.log(response.data) // mockData
        console.log(response.status) // 200
      })
  }
}
.......
```

# 5. [Vuex](https://vuex.vuejs.org/ja/api.html)のインストールとStoreの書き換え

[コード](https://github.com/MariMurotani/my-project/tree/05_vue-ajax)

```sh
npm install vuex install --save
```

## 5.1 非同期通信の実践

まず、PropertyStore.jsをVuexに合わせて書き換えていきます

```javascript:PropertyStore.js
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex)

//  データストア
var PropertyStore = new Vuex.Store({
  state: {
    property: {
      nickname: 'Loading.....'
    }
  },
  mutations: {
    increment (state, n) {
      state.count += n
    },
    getUser (state, response) {
      state.property.nickname = response.data.nickname
    },
    setUser (state, nickname) {
      state.property.nickname = nickname
    }
  },
  actions: {
    getUser ({ commit }) {
      axios.get('http://127.0.0.1:4321/profile_wait')
        .then(response => {
          if (response.status === 200) {
            commit('getUser', response)
          }
        })
    },
    setUser ({ state, commit, rootState }) {
      if (state.property.nickname.length <= 3) {
        throw new Error('3文字以上入力してください')
      }
      axios.post('http://127.0.0.1:4321/profile')
        .then(response => {
          if (response.status === 200 && response.data.status === 'ok') {
            commit('setUser', state.property.nickname)
          } else {
            throw new Error('レスポンスエラー')
          }
        })
    }
  }
})
export default PropertyStore
```

次に、Scene1に処理を追加します。

```javascript:component/Scene1.vue
.....
import InputText from './forms/InputText'
import PropertyStore from '../models/PropertyStore.js'

export default {
  created: function () {

  }
}
//  ユーザー情報取得
PropertyStore.dispatch('getUser')
//  コンポーネントを登録
Vue.component('nickname-header', NickNameHeader)
.....
```

最後に、ボタンアクションでバリデーションしていた部分を書き換えます。

```javascript:component/forms/Scene1.vue
.....
  methods: {
    validate: function (event) {
      try {
        this.$data.sharedState.dispatch('setUser')
        this.$router.push('/scene2')
      } catch (e) {
        alert(e.message)
      }
    }
  }
.....
```

スタブでレスポンスの返却を1秒まってから行っているので、初期値としてnicknameに入れているloadingという文字が1秒後に書き換えられる事が確認できました。

![0.gif](https://qiita-image-store.s3.amazonaws.com/0/89997/2b54e9f5-598a-779e-5655-27dc1d1c7c6d.gif)


# 6. Loading画面からのフォーム表示
上記 5.の実習では、フォームを表示した後にデータ入力が可能な状態になるので、ユーザービリティがよくありませんでした。
ここで、再度リファクタリングをしてStoreのステータス管理とUIを結びつけて行きます。
ここでは、[v-show](https://jp.vuejs.org/v2/guide/conditional.html#v-if-vs-v-show)　というsyntaxを利用します。

[コード](https://github.com/MariMurotani/my-project/tree/06_vue-ajax_refactor)

まずは、ステータス管理用のconfigを作成します。

```javascript:config/Status.js
export const StatusConfig = {
  INITIALIZE: 0,
  DATA_LOADED: 1
}
```

PropertyStoreでステータス管理ができるようにコードを書き換えます。
1. 先程作成した、StatusConfigをインポートします。
2. stateの中にstatus=StatusConfig.INITIALIZEを追加、代入しましょう。
3. mutations内のgetUserの中でstatusをStatusConfig.DATA_LOADEDに書き換えます。

```javascript:models/PropertyStore.js
import {StatusConfig} from '../config/Status.js'

.....
  state: {
    status: StatusConfig.INITIALIZE,
    property: {
      nickname: 'Loading.....'
    }
  },
.....
  mutations: {
.....
    getUser (state, response) {
      state.property.nickname = response.data.nickname
      state.status = StatusConfig.DATA_LOADED
    },
```

最後にScene1.vueを書き換えます。
1. StatusConfigのインポート
2. [computed](https://jp.vuejs.org/v2/guide/computed.html)の中にis_loading関数を追加
3. &lt;template&gt;&lt;/template&gt;内でLoading中に表示するものとLoading後に表示するものを書き分けます。

```html:components/Scene1.vue
<template>
  <div class="scene1">
    <div v-show="is_loading">
      Loading......
  </div>
  <div v-show="!is_loading">
      <nickname-header></nickname-header>
      <input-text></input-text>
      <input-button></input-button>
    </div>
  </div>
</template>
........
import PropertyStore from '../models/PropertyStore.js'
import {StatusConfig} from '../config/Status.js'
........
export default {
  computed: {
    is_loading: function () {
      return PropertyStore.state.status === StatusConfig.INITIALIZE
    }
  }
}
........
``````

![さいごの.gif](https://qiita-image-store.s3.amazonaws.com/0/89997/597ff439-727e-0306-07e6-bc53ae028ee0.gif)