こんにちは、あかしぃです。
Webサービスの会員登録フォームで、郵便番号を入力したら自動的に住所も入力される…みたいなフォームを見たことがあると思います。
Vue.jsやNuxt.jsならこういったフォームを簡単に作れるので、コードを含めて解説します。
まずソースコードのサンプルがこちら。住所取得apiには「zipcloud」を利用しています。
<template>
// 郵便番号の入力フォーム
<input
v-model="zipcode"
type="number"
placeholder="0009999"
@blur="fetchAddress" />
// 都道府県入力フォーム(セレクトボックスでも可)
<input
v-model="prefecture"
type="text"
placeholder="東京都" />
// 都道府県以下の住所入力フォーム
<input
v-model="address"
type="text"
placeholder="渋谷区xxxxxx" />
</template>
<script>
import axiosJsonpAdapter from 'axios-jsonp'
const ZIPCODE_API_URL = 'http://zipcloud.ibsnet.co.jp/api/search'
export default {
data() {
return {
zipcode: '',
prefecture: '',
address: ''
}
},
methods: {
async fetchAddress() {
// 郵便番号のバリデーションチェック
const reg = /^\d{7}$/
if (!reg.test(this.zipcode)) return
// 住所apiを叩く
const res = await this.$axios.$get(ZIPCODE_API_URL, {
adapter: axiosJsonpAdapter,
params: {
zipcode: this.zipcode
}
})
// 存在しない郵便番号でapiを叩くと200以外のステータスが返ってくる
if (res.status !== 200) return
// 返却されたデータを挿入する
this.prefecture = res.results[0].address1
this.address = res.results[0].address2 + res.results[0].address3
}
}
}
</script>
コードについて、簡単に解説します。
入力フォームそのものはただのHTMLなので、とくに解説する点はありません。
v-modelでそれぞれのデータをバインディングしているのと、@blurで入力フォームからフォーカスが外れたタイミングでfetchAddressが実行されるようにしています。
サンプルコードでは都道府県の部分もinputタグを使っていますが、セレクトボックス形式にして一覧から都道府県を選択できるようにしてあげたほうがユーザーにとっては使いやすいかも(どうせ自動入力されるのであまり意味はないかもですが・・・)
セレクトボックスにする場合は、prefecture.jsonといった名前で都道府県一覧のデータをリポジトリに含め、それをvuex(store)に登録すると使いやすいです。使い方のイメージはこんな感じ。
mapGettersなどで、prefecturesで都道府県.jsonの中身を取得できるようにしている前提
<select
v-model="prefecture"
placeholder="都道府県を選択">
<option
v-for="(prefecture, index) in prefectures"
:key="index"
:label="prefecture.name"
:value="prefecture.name" />
</select>
住所の自動入力については、fetchAddressメソッドが担当しています。
const reg = /^\d{7}$/
if (!reg.test(this.zipcode)) return
// if (!/^\d{7}$/.test(this.zipcode)) return と書くと1行でも書ける
この部分は郵便番号のバリデーションです。この処理がなくても問題ありませんが、無意味なapiリクエストを送りたくないので、郵便番号ではない形式の入力があった場合は、この時点で処理を中断しています。
ハイフンつきを許すなら、正規表現部分を少し変更する必要があります。
const res = await this.$axios.$get(ZIPCODE_API_URL, {
adapter: axiosJsonpAdapter,
params: {
zipcode: this.zipcode
}
})
外部apiからデータを取得する処理です。
zipcloudのリファレンスを読むと、データを取得するには「http://zipcloud.ibsnet.co.jp/api/search/」にzipcode=xxxxxxxというクエリをつけてGETメソッドで叩けばいいことがわかります。
なので、「this.$axios.$get(`ZIPCODE_API_URL?${this.zipcode}`)」のような形式でリクエストを送ればいい…予定だったのですが、zipcloudにはCROS対策が行われていないようです。
なので、別ドメインからapiを叩こうとするとCROSエラーが発生します。
こういったケースではJsonpを使います。(Jsonpについてはこちらの記事とかがわかりやすい)
axiosでJsonpリクエストを送るには、「axios-jsonp」というライブラリが必要です。サンプルコードの先頭で、「import axiosJsonpAdapter from 'axios-jsonp'
」としている部分ですね。(先にnpm i axons-jsonpしておきましょう)
こいつをaxiosのオプションとしてセットしてあげれば、無事CROSエラーを回避して、データを取得することができます。
ここでもう一度、zipcloudのリファレンスを見て、返ってくるデータ構造を確認すると、以下のような構造であることがわかります。
{ "message": null, "results": [ { "address1": "北海道", "address2": "美唄市", "address3": "上美唄町協和", // 以下にデータが続く } ] }
返ってくるデータ構造がわかれば、あとはそれに対応した処理を書くだけです。
// 存在しない郵便番号でapiを叩くと200以外のステータスが返ってくる
if (res.status !== 200) return
// 返却されたデータを挿入する
this.prefecture = res.results[0].address1
this.address = res.results[0].address2 + res.results[0].address3
prefecture、addressはそれぞれinputタグにバインディングされているので、データを入れてあげればブラウザにも反映され、ユーザーから自動的に住所が入力されたように見える…というわけですね。
自分で実装するまでは、「これどうやって作ってんのかな〜…」みたいに思っていましたが、書いてみると意外にシンプルでした。そして、一般的な会員登録フォームではもはやデフォルトの仕様になっている気がしますね。
April 13, 2019 - posted by akashixi