Help us understand the problem. What is going on with this article?

Vue.jsでAPI通信

Index

1.VueCLIでHelloWorld
2.Vue.jsでフォームを使おう
3.Vue.jsで単一ファイルコンポーネント
4.Vue.jsでAPI通信
5.Vue.jsで猫検索アプリ作成
6.おまけ


1. 前提条件

前回の実習を終わらせておくこと

2.axiosmockeyをインストール

VueでAjaxするには何を使えばいいの?っていう話が調べていくときな臭い感じ・・・・・
https://jp.vuejs.org/2016/11/03/retiring-vue-resource/
とりあえず、上記の公式コミュニティでおすすめされてる、axiosを使ってみたいと思います。

npm install axios --save
npm install -D mocky --save

3. mokeyのインストールと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 動作確認

# 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を読んでいるので少し遅れてコンソールにレスポンスが出力されます。

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のインストールとStoreの書き換え

コード

npm install vuex install --save

5.1 非同期通信の実践

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

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に処理を追加します。

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)
.....

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

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

6. Loading画面からのフォーム表示

上記 5.の実習では、フォームを表示した後にデータ入力が可能な状態になるので、ユーザービリティがよくありませんでした。
ここで、再度リファクタリングをしてStoreのステータス管理とUIを結びつけて行きます。
ここでは、v-show というsyntaxを利用します。

コード

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

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に書き換えます。

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の中にis_loading関数を追加
3. <template></template>内でLoading中に表示するものとLoading後に表示するものを書き分けます。

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした