Laravel + Nuxtで現在地から最寄りのイベントを取得する(Nuxt編)

# 概要
[前回のAPIに対して](https://qiita.com/gemetasu1103/items/5ce523700beae4828b13)Nuxt側で現在位置情報を取得し、リクエストを投げ最寄りのイベント情報を取得する

### Nuxt側の処理の流れ
ヘッダ部に現在参加しているイベント名が表示されている箇所があり
そこをタップするとイベント選択画面に遷移する

![スクリーンショット 2020-04-19 10.52.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305479/b7bd4c9f-a1c5-0ad4-7680-cd74979452d8.png)

遷移後の初期に表示時に現在位置情報を取得し、参加可能なイベント一覧をAPIから取得し、プルダウンに反映する

![スクリーンショット 2020-04-19 10.54.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305479/dc4b14e6-cad6-91c8-bea0-ecb87d17f7d0.png)


# 環境
Laravel 5.8
Nuxt 2.5.1

# 参考サイト

[Nuxt.js / Vue.js で現在地情報を取得する](https://note.com/tana_d/n/nce5ce09e55bd)

# 実装内容

### 現在位置情報を取得する
現在地を取得するには、SSR側ではなくCSR側で実行される必要がある。
そのため、beforeMountで実行する

```client/mixins/fetchEvent.js
/*
   現在地情報を元にイベント情報を取得する
 */
export default {
  beforeMount() {
    if (process.client) {
      if (!navigator.geolocation) {
        alert(
          '現在地情報を取得できませんでした。お使いのブラウザでは現在地情報を利用できない可能性があります。'
        )
        return
      }

      const options = {
        enableHighAccuracy: false,
        timeout: 5000,
        maximumAge: 0
      }

      navigator.geolocation.getCurrentPosition(
        this.success,
        this.error,
        options
      )
    }
  },

  methods: {
    // getCurrentPositionが成功した際に呼び出される関数
    async success(position) {
      // 現在地を元にイベント情報を設定する
      await this.$store.dispatch('event_info/fetchEvents', position.coords)
    },

    // getCurrentPositionが失敗した際に呼び出される関数
    error(error) {
      switch (error.code) {
        case 1: // PERMISSION_DENIED
          alert('位置情報の利用が許可されていません')
          break
        case 2: // POSITION_UNAVAILABLE
          alert('現在位置が取得できませんでした')
          break
        case 3: // TIMEOUT
          alert('タイムアウトになりました')
          break
        default:
          alert('現在位置が取得できませんでした')
          break
      }
    }
  }
}

```

現在地取得が成功したら下記のVuexのアクションを呼び出して、イベント一覧を取得する。
`await this.$store.dispatch('event_info/fetchEvents', position.coords)`

```client/store/event_info.js
export const getters = {
  eventItems(state) {
    return state.event_items
  }
}

export const state = () => ({
  event_items: {}
})

export const mutations = {
  SET_EVENT_ITEMS(state, eventItems) {
    state.event_items = eventItems
  }
}

export const actions = {
  fetchEvents({ commit, app, getters }, coords) {
    this.$axios
      .get(
        `/api/user/event?latitude=${coords.latitude}&longitude=${
          coords.longitude
        }`
      )
      .then(response => {
        commit('SET_EVENT_ITEMS', response.data.data)
      })
  }
}

```


### 画面側の実装

イベント取得するために先程実装した`fetchEvent.js`を使用する
イベントを選択したら、イベント選択状態を更新しTOP画面に遷移する

```client/pages/client/event/index.vue
<template>
  <section>
    <b-tabs position="is-centered">
      <template>
        <!-- 開催中イベントタブ -->
        <b-tab-item label="開催中のイベント">
          <form class="form" @submit.prevent="activeEventSelect">
            <!-- 開催中のイベントプルダウン -->
            <div class="field">
              <div class="select is-primary is-fullwidth">
                <select v-model="event_selected">
                  <option
                    v-for="event_item in eventItems"
                    :key="event_item.id"
                    :value="event_item"
                  >
                    {{ event_item.event_name }}
                  </option>
                </select>
              </div>
            </div>

            <!-- イベント選択ボタン -->
            <div class="field-button">
              <input
                type="submit"
                class="button is-blue is-large is-fullwidth"
                value="選択"
              />
            </div>
          </form>
        </b-tab-item>
        <!-- 終了したイベントタブ -->
        <b-tab-item label="終了したイベント">
          終了したイベントでしう
          <!-- 開催中のイベントプルダウン -->
          <div class="field">
            <div class="select is-primary is-fullwidth">
              <select>
                <option
                  v-for="prefItem in pref"
                  :key="prefItem.toString()"
                  :value="prefItem.toString()"
                >
                  {{ prefItem }}
                </option>
              </select>
            </div>
          </div>
        </b-tab-item>
      </template>
    </b-tabs>
  </section>
</template>

<script>
import jsonPrefData from '~/assets/json/pref_city.json'
import fetchEvent from '@/mixins/fetchEvent.js'

export default {
  layout: 'client/simple',
  mixins: [fetchEvent],

  data() {
    return {
      event_selected: '',
      pref: jsonPrefData,
      prefSearchForm: {
        pref: '',
        city: ''
      }
    }
  },

  methods: {
    // 現在選択してるイベントを更新する
    activeEventSelect() {
      this.$store.dispatch('event_info/setEvent', this.event_selected)
      this.$store.dispatch(
        'event_info/setCurrency',
        this.event_selected.currency_id
      )
      this.$store.dispatch(
        'event_info/setEventName',
        this.event_selected.event_name
      )
      this.$store.dispatch('event_info/fetchUserWallet', {
        loggedIn: this.authenticated,
        userId: this.$nuxt.$auth.user.id,
        currencyId: this.event_selected.currency_id
      })

      this.$toast.open({
        duration: 3000,
        message: this.event_selected.event_name + 'を選択しました。',
        type: 'is-success'
      })
      this.$router.push('/')
    }
  }
}
</script>

<style lang="scss" scoped></style>

```