🤭

Vue+ぐるなびAPIを使ってレストラン情報を取得した

2020/09/22に公開

はじめに

Vue(axios)を使ってAPIからjsonファイルを取得する方法の一例を紹介します。
ぐるなびAPIは無料で始められるので、お勉強用にとてもいいと思います。

最終的にできるもの

環境

無駄なパッケージたくさんあると思います。
エディタはphpstormを使っています。
pakage.json

  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint",
    "dev": "webpack"
  },
  "dependencies": {
    "core-js": "^3.6.5",
    "vue": "^2.6.11",
    "vue-axios": "^2.1.5"
  },
  "devDependencies": {
    "@fortawesome/fontawesome-free": "^5.14.0",
    "@vue/cli-plugin-babel": "~4.5.0",
    "@vue/cli-plugin-eslint": "~4.5.0",
    "@vue/cli-service": "~4.5.0",
    "axios": "^0.19",
    "babel-eslint": "^10.1.0",
    "eslint": "^6.7.2",
    "eslint-plugin-vue": "^6.2.2",
    "jquery": "^3.2",
    "node-sass": "^4.14.1",
    "sass": "^1.15.2",
    "vue-template-compiler": "^2.6.11"
  },

ディレクトリ構成

-src
 --conponent
  --Gurunabi.vue
App.vue
main.js

conponent Vueファイル以外のファイル状況

App.vue

<template>
  <div id="app">
    <Gurunabi></Gurunabi>
  </div>
</template>
<script>
  import Gurunabi from "@/components/Gurunabi";
  export default {
    components:{
      Gurunabi,
    },
  }
</script>

main.js

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')

ぐるなびAPIについて

こちらのリンクからjsonファイルのテストツールがあるので受け取るjson形式を確認することができます。
(地味にこれ便利ですよね)
https://api.gnavi.co.jp/api/tools/

Gurunabi.vue

<template>
  <div>
    <div>
      <h3>近くの飲食店を検索できます</h3>
      <input type="text" id="name" v-model="name" placeholder="店名"><br>
      <p v-if="name">
        <button @click="searchShop" class="shop-get-button">検索</button>
      </p>
      <input type="text" id="freeWord" v-model="freeWord" placeholder="フリーワード"><br>
      <p v-if="freeWord">
        <button @click="searchFree" class="shop-get-button">検索</button>
      </p>
      <hr>
    </div>
    <h3>検索結果を表示</h3>
    <div v-for="info in shopInfo" :key="info.id">
      <div class="main-block">
        <div class="top-block">
          <div class="side-pic">
            <a :href="info.url" target="_blank">
              <img :src=info.image_url.shop_image1 alt="">
            </a>
          </div>
          <div class="comment-area">
            <h3>{{info.name}}</h3>
            <p><b>{{info.address}}</b></p>
            <p><b>{{info.tel}}</b></p>
            <p><b>{{info.opentime}}</b></p>
          </div>
        </div>
        <div class="pr-comment">
          <p>{{info.pr.pr_long}}</p>
          <button :href=info.url target="_blank" class="shop-get-button">お店の予約</button>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import axios from 'axios'
export default {
  data(){
    return{
      name:'',//入力値から取得
      freeWord:'',//入力値から取得
      baseurl:'https://api.gnavi.co.jp/RestSearchAPI/v3/?',
      id:'keyid=APIKEY',
      shopInfo:[],
    }
  },
  methods:{
    searchShop:function(){
        let url= this.baseurl + this.id + '&name=' + this.name;
        axios.get(url)
      .then(function (response) {
          this.shopInfo = response.data.rest
        }.bind(this))
          .catch(function (error) {
            console.log(error);
            alert('検索結果は見つかりませんでした');
          })
    },
    searchFree:function(){
      let url1= this.baseurl + this.id + '&freeword=' + this.freeWord;
      axios.get(url1)
          .then(function (response) {
            this.shopInfo = response.data.rest
          }.bind(this))
          .catch(function (error) {
            console.log(error);
            alert('検索結果は見つかりませんでした');
          })
    },
  },
}
</script>

<style scoped>
.shop-get-button{
  position: relative;
  display: inline-block;
  padding:0.25em 0.5em;
  text-decoration: none;
  color:#fff;
  background: #fd9535;
  border-radius: 4px;
  -moz-border-radius: 4px;
  -webkit-border-radius: 4px;
  box-shadow: inset 0 2px 0 rgba(255,255,255,0.2),inset 0 -2px 0 rgba(0,0,0,0.05);
  -moz-box-shadow: inset 0 2px 0 rgba(255,255,255,0.2),inset 0 -2px 0 rgba(0,0,0,0.05);
  -webkit-box-shadow: inset 0 2px 0 rgba(255,255,255,0.2),inset 0 -2px 0 rgba(0,0,0,0.05);
  font-weight:bold;
  border: solid 2px #d27d00;
}
.shop-get-button:active{
  box-shadow: 0 0 2px rgba(0,0,0,0.30);
  -moz-box-shadow: 0 0 2px rgba(0,0,0,0.30);
  -webkit-box-shadow: 0 0 2px rgba(0,0,0,0.30);
}
.main-block{
  background: #fff;
  width:960px;
  margin:100px auto;
  border: 0.5px solid #2c3e50;
}
.top-block{
  overflow: hidden;
}
.side-pic{
  width:30%;
  float: left;
}
.side-pic:hover{
  cursor: pointer;
}
.comment-area{
  width:70%;
  box-sizing: border-box;
  padding:29px;
  float:left;
  text-align: left;
}
.pr-comment{
  width:100%;
  box-sizing: border-box;
  padding:20px;
  background: #f6f5f4;
}
input[type="text"]{
  width:30%;
  padding:10px;
  border: .5px solid #2c3e50;
}
</style>

主要箇所をざっくりと説明

 <input type="text" id="name" v-model="name" placeholder="店名"><br>
------------------------------------------------------------------------
data(){
    return{
      name:'',//入力値から取得
------------------------------------------------------------------------

let url= this.baseurl + this.id + '&name=' + this.name;
        axios.get(url)

テキスト入力した値をURLの引数にして、APIからjson形式で取得しています。

  axios.get(url)
      .then(function (response) {
          this.shopInfo = response.data.rest
------------------------------------------------------------------------
data(){
    return{
      name:'',//入力値から取得
      freeWord:'',//入力値から取得
      baseurl:'https://api.gnavi.co.jp/RestSearchAPI/v3/?',
      id:'keyid=API KEY,
      shopInfo:[],
    }
------------------------------------------------------------------------

   <div v-for="info in shopInfo" :key="info.id">
      <div class="main-block">
        <div class="top-block">
          <div class="side-pic">
            <a :href="info.url" target="_blank">
              <img :src=info.image_url.shop_image1 alt="">
            </a>
          </div>
          <div class="comment-area">
            <h3>{{info.name}}</h3>
            <p><b>{{info.address}}</b></p>
            <p><b>{{info.tel}}</b></p>
            <p><b>{{info.opentime}}</b></p>
          </div>
        </div>
        <div class="pr-comment">
          <p>{{info.pr.pr_long}}</p>
          <button :href=info.url target="_blank" class="shop-get-button">お店の予約</button>

配列形式で受け取った値をtemplate内でv-forを使って回して動的に表示しています。
"info.url"などはパラメータのためv-bindを使用して動的に表示させています。

つまづいたPOINT

  • v-bindをあまり理解できておらず、template内で表示させるときに<img src="{{info.url}}" >とやってしまっていました。
    最初はbsse64かな?と思ったりしてつまづきました。

Discussion