"BOKU"のITな日常

還暦越えの文系システムエンジニアの”BOKU”は新しいことが大好きです。

プラグインを作ってみる。thisで参照とコンテキストで参照の2種類ぶん。/NUXT自己流チュートリアル(5)

今回は、Nuxt.jsで「プラグイン」を2種類作って、asycData()とmethodsの各ブロックの中で使ってみます。

f:id:arakan_no_boku:20190509225706p:plain

 

事前に頭にいれといた方がいいこと

 

Nuxt.js では JavaScript プラグインを定義して使うことができます。

これには「axios」など外部プラグインもありますが、当然自前で作ることも可能です。

自前で作るプラグインは「plugins」フォルダに、拡張子「.js」で作ります。

Nuxt.jsのプラグインは感覚的にはサブルーチンみたいな感じなんですが、他の言語のそれとは大きく違う特徴が2つあります。

  • 定義の仕方で使える場所が異なること
  • 使うときには「nuxt.config.js」に書いておく必要があること

です。

 

定義の仕方で使える場所が異なること

 

Nuxt.jsには。

  • asyncData()みたいにページコンポーネントがロードされる前に実行されるもの(=thisが使えない)
  • methods内の関数のようにロード後に実行されるもの(=thisが使える)

があります。

そのどちらで使用する想定のプラグインかによって、定義の仕方が違う・・というのが、最初とまどうところです。

具体的にどう違うのか?は後でソースコードと一緒に確認するのですが、とりあえず、これを前提として頭にいれとくと、わかりやすいです。

 

使うときには「nuxt.config.js」に書いておく必要があること

 

pluginsフォルダに.js拡張子でファイルを作成したものを、pagesのvueファイル内で使えるようにするには、「nuxt.config.js」の「plugins」ブロックに書いておく必要があります。

ここに書くのを忘れてたり、書き方が間違ってたりすると「not function(ファンクションじゃねーぞ!)」と怒られます。

他の言語(特にpythonとか)やってると、importとかで書いてしまいそうになるかもしれないので念のため(自分だけかな?)。

 

チュートリアル実行の前提

 

自己流チュートリアル(1)で「前提」と「チュートリアルの準備」として書いていることは、できている必要があります。

つまり。

インストール・環境設定・テスト専用Chromeショートカットの作成うんぬんですね。

まだの方は、下記記事を先に参照して、環境設定をお願いします。

arakan-pgm-ai.hatenablog.com 

あと、都道府県・市町村データ取得のWEB-APIを使います。

API-KEYが必要になるので、まだ取得していない場合は、以下で登録してAPI-KEYを取得しておいてください。

opendata.resas-portal.go.jp

 

今回やってみること

 

NUXT自己流チュートリアル(4)で都道府県情報・市町村情報共、Selectにして、都道府県を選択すると連動して市町村のSelectの内容を変更する・・という、まあ、よくあるパターン・・をやりました。

それでちゃんと動くのですが、ちょっとソースの記述量が増えてガチャガチャします。

画面である関係上、<templete>内の記述は多くなるので、できるだけ<script>内はすっきりさせたい・・ということで、都道府県情報・市区町村情報をWEB-APIから取得してくる処理を「プラグイン」化して、全体をすっきりさせようというのが、今回の目標になります。

具体的には、NUXT自己流チュートリアル(4)だと、こんな感じでした。

pages/cityapi.vue 

<script>
import axios from 'axios'
export default {
  async asyncData() {
    return await axios
    .get('https://opendata.resas-portal.go.jp/api/v1/prefectures',{
      headers:{
        "X-API-KEY":"XXXXXXXXXXXXXXXXXXOjK"
      },
      data:{}
    })
    .then((res) => {
      return { prefecturesJson: res.data }
    })
  },
  methods:{
    async updateMunicipalitiesJson({prefecturesSelected}){
      let url =  `https://opendata.resas-portal.go.jp/api/v1/cities?prefCode=${prefecturesSelected} `
      return await axios
      .get(url,{
        headers:{
          "X-API-KEY":"XXXXXXXXXXXXXXXXXXXXOjK"
        },
        data:{}
      })
      .then((res) => {
        this.municipalitiesJson =  res.data
      })
    }  
  },
</script>

それを今回はプラグインに外だしすることで。 

<script>
export default {
  async asyncData(context) {
   return {prefecturesJson: await context.app.$getPrefecturesJson()}
  },
  methods:{
    async updateMunicipalitiesJson({prefecturesSelected}){
      this.municipalitiesJson = await this.$getmunicipalitiesJson(prefecturesSelected)
    }  
  },
}
</script>

こんな感じにします。

見通しがよくなりますし、何の処理かが明確で、とても良い感じになります。

では、やっていきます。

 

プラグインで外だしにするソースコード

 

今回は、以下の2種類のプラグインを作ります。

両方とも、プラグインの関数名に「$」をつけてますが、これは名前の衝突をさけるための「一般的な命名ルール」に従ってます。

別に$を付けない名前にしても問題なく動くのですが、別に無理に$をつけない名称にする理由もありませんので。

 

プラグインを「this」を使って参照する。

 

今回の場合だと、onChangeイベントをトリガにして呼ばれる「updateMunicipalitiesJson」内で呼び出す「$getmunicipalitiesJson」がそれにあたります。

まず、ソースコードです。

plugins/getMunicipalitiesJson.js

import Vue from 'vue'
import axios from 'axios'

 Vue.prototype.$getmunicipalitiesJson = (prefecturesSelected) => {
    let url =  `https://opendata.resas-portal.go.jp/api/v1/cities?prefCode=${prefecturesSelected} `
    return axios
    .get(url,{
      headers:{
        "X-API-KEY":"XXXXXXXXXXXXXXXXXXOjK"
      },
      data:{}
    })
    .then((res) => {
      return res.data
    })
}
 

ポイントを補足します。

関数名の定義の仕方がポイントです。

Vue.prototype.$getmunicipalitiesJson = (prefecturesSelected) => {

ちょっと独特の形ですが、上記のようにします。

ざっくり言えば「Vue.prototype」に関数名を記述すれば、thisで参照できる関数になるよ・・ということです。

axiosを使った処理の中身については、ほぼ、元のコピペなのですが、以下の2点を変更しています。

  • async/awaitをとっている。
  • this.municipalitiesJson = res.dataではなく、「return res.data」にしている。

理由は、見たらわかるレベルなので補足はしません。

プラグインに外だししたわけですから、当然の処理をしてるだけです。

 

プラグインを「context.app」を使って参照する。

 

今度は、thisの使えない「asyncData()」の中でプラグインの関数を参照する方です。

context.app.$getPrefecturesJson()のように、「context.app」を使って参照します。

ソースコードです。

plugins/getPrefecturesJson.js

import axios from 'axios'

export default ({ app }, inject) => {
    app.$getPrefecturesJson = () => {
        return axios
        .get('https://opendata.resas-portal.go.jp/api/v1/prefectures',{
        headers:{
            "X-API-KEY":"XXXXXXXXXXXXXXXXXXXXXOjK"
        },
        data:{}
        })
        .then((res) => {
            return res.data
        })
    }
}

ポイントです。 

関数名を定義する部分が以下のようになっています。

export default ({ app }, inject) => {

    app.$getPrefecturesJson = () => {

    }

このappに関数名を記述することで、context.appで参照できるようになります。

とりあえず、この形を覚えておきます。

context.appというのは 「すべてのプラグインを含むルートの Vue インスタンス」なので、そこに加えてやる感じですかね。

ja.nuxtjs.org

こちらのaxiosを使った処理の内容は、ほぼ元のコピペです。

上記と同様に、async/awaitだけはずしてます。

 

プラグインを登録する

 

上記で2つのプラグインを作ったわけですが、そのままでは使えません。

プロジェクト内のVueソース内で使うため、nuxt.config.jsに登録する必要があります。

以下のように書きます。

nuxt.config.js

plugins: [
   { src:'~/plugins/getMunicipalitiesJson.js'},
   { src:'~/plugins/getPrefecturesJson.js'}
],

書き方はもう一種類あります。

こういう書き方です。

plugins: ['~/plugins/getMunicipalitiesJson.js']

実際、サンプルとかでも両方の書き方が混在しています。

でも。

混在しない方が良いと思っているので、自分は「 { src:'~/plugins/getMunicipalitiesJson.js'}」の書き方で統一しています。

 

上記のプラグインを反映させたpagesのソース

 

プラグインを利用するように変更した「cityapi.vue」です。

pages/cityapi.vue

<template>
  <div>
    <NLink to="/">
      ルートのページへ戻ります
    </NLink>
    <br/>
    <img src="/jamap.JPG" alt="image03" />
    <div v-if=" prefecturesJson.message == null">
        <select v-model="prefecturesSelected" v-on:change="updateMunicipalitiesJson({prefecturesSelected})">
          <option value=""></option>
          <option v-for="pOption in prefecturesJson.result" v-bind:key="pOption.id" v-bind:value="pOption.prefCode">
            {{pOption.prefName}}
          </option>
        </select>    
    </div>
    <div v-if="municipalitiesJson != null">
      <div v-if="municipalitiesJson.message == null">
        <select v-model="municipalitiesSelected">
          <option v-for="mOption in municipalitiesJson.result" v-bind:key="mOption.id" v-bind:value="mOption.cityCode">
            {{mOption.cityName}}
          </option>
        </select>    
      </div>
    </div>  
    <div v-else>
        <select v-model="municipalitiesSelected">
          <option value=""></option>
        </select>  
    </div>  
  </div>
</template>

<script>
export default {
  async asyncData(context) {
   return {prefecturesJson: await context.app.$getPrefecturesJson()}
  },
  data(){
    return{
      prefecturesSelected:'',
      municipalitiesSelected:'',
      municipalitiesJson:null
    }
  },
  methods:{
    async updateMunicipalitiesJson({prefecturesSelected}){
      this.municipalitiesJson = await this.$getmunicipalitiesJson(prefecturesSelected)
    }  
  },
  head: {
    title: 'CityApi page'
  }
}
</script>
 

特に補足はありません。 

<templete>内は、NUXT自己流チュートリアル(4)と同じですし、<script>内は上で説明している通りですので。

cityapi.vueと今回追加したプラグイン関連のファイル以外のソースについても変更はありません。

about.vue以外は「NUXT自己流チュートリアル(2)」。

about.vueは「NUXT自己流チュートリアル(3)」で変更したままです。

動かすのに必要な場合は、各ページを参照ください。 

arakan-pgm-ai.hatenablog.com

arakan-pgm-ai.hatenablog.com

arakan-pgm-ai.hatenablog.com

 

さて実行してみます

 

プロジェクトをカレントフォルダにして「npm run dev」して、「http://localhost:3000」で表示します。

もちろん、Originチェックを回避できる「テスト用ショートカット」から起動したものですよ。

動きそのものは、NUXT自己流チュートリアル(4)と同じです。

f:id:arakan_no_boku:20190516234656p:plain

ここから「市区町村のサンプル」のリンクをクリックします。

f:id:arakan_no_boku:20190516234814p:plain

都道府県のSELECTを開いたところです。

ここで、東京でも選んでみます。

f:id:arakan_no_boku:20190516234944p:plain

変化がないことを確認できればOKです。

今回は、こんなところで。

ではでは。