"BOKU"のITな日常

BOKUが勉強したり、考えたことを頭の整理を兼ねてまとめてます。

Nuxtアプリ開発で使うVue.jsのディレクティブ・テンプレート構文/API-KEYが必要なWeb-Apiの利用など

f:id:arakan_no_boku:20190509225706p:plain

目次

はじめに

リライトして目次を追加しました。

内容は2019年5月当時のままです。

僕がNuxt.jsの各機能を理解するために、ひとつひとつ確認したことをまとめていこうと考えています。

インストール・環境構築は、以下の手順でできている前提です。

arakan-pgm-ai.hatenablog.com

Nuxt.jsを整理する方針

Nuxt.js関連資料やサンプルソースを見ると混乱することが多いのは、Nuxt.jsはVue.jsの機能を包含していて、ひとつのソースの中で、どれがNuxt.js特有の機能で、どれがVue.jsの機能かの区別がつけにくいからだと感じています。

その混在しているイメージを図にしてみると、こんな感じに見えます。

f:id:arakan_no_boku:20190511100242p:plain

なので、僕はアプローチとして、各技術要素にわけて、一回に少しずつ要素を確認していくことにしました。

分け方としては、以下のようになると考えています。

  1. Nuxt.js独自の要素ー基本
  2. Vue.js独自の要素
  3. BulmaのCSS要素(Burfyのclassは、Bulmaを使っている場合が多い)
  4. Buefy独自の要素
  5. Nuxt.js独自の要素ー応用
  6. テストフレームワーク他モジュールの要素

このうち、今回は「Vue.js独自の要素」をとりあげます。

今回は以下のようなことをやってみて。

  • 郵便番号APIを使って、正常(200)の場合だけ、情報を表示する。
  • 市区町村APIを使って、取得した市区町村をテーブルにリストする。
  • 変数を使ってURLを渡して外部リンクを機能させる。
  • システム日付を取得して今日の日付を表示してみる。

ここで以下を重点的に確認します。

  • Nuxt.jsはVue.jsの開発フレームワークであること
  • テンプレート構文とScriptタグ内で利用する機能

 

Nuxt.jsはVue.jsの開発フレームワークであること

Nuxt.jsは「Vue.jsアプリケーションを開発するためのフレームワーク」です。

なので。

  • Vue.jsのディレクティブ(v-で始まり、HTMLタグ内で使う特別な属性)
  • テンプレート構文(”Mustache” 構文(二重中括弧{{}})で変数を参照するなど)

を使います。

とりあえず、「テンプレート内で利用する機能」と「Scriptタグ内で利用する機能」にわけて整理してみます。 

テンプレート構文とScriptタグ内のブロックの意味合いと役割

テンプレート構文で使用するディレクティブを一覧で整理します。

テンプレート構文:条件付きレンダリング(条件分岐)
ディレクティブ ふるまい
v-if ブロックを条件に応じて描画したい場合に使用する。
ブロックは、ディレクティブの式が真を返す場合のみ描画されます。
v-else v-if に対する “else block” を示すために、v-else ディレクティブを使用します:
v-else-if v-if の “else if block” として使用します。複数回連結することができます:
v-show v-show はシンプルに要素の display CSS プロパティを切り替えます。
注意事項 v-forディレクティブと一緒に使わないこと

  

テンプレート構文:リストレンダリング(繰り返し)
ディレクティブ ふるまい
v-for 配列の要素にマッピングする。
v-for=""item in items""
{{ item.message }}
v-for 配列の要素にマッピングする。
v-for=""(item, index) in items""
{{ item.message }}{{ index }}
v-for オブジェクトにマッピングする
v-for=""value in object""
{{ value }}
v-for オブジェクトにマッピングする
v-for=""(value, name) in object""
{{ value }}{{ name }}
注意事項 v-ifディレクティブと一緒に使わないこと

  

テンプレート構文:データバインディング(データ参照他)
構文 ふるまい
{{ boo }} 変数「boo」の参照。
{{}}はmustache タグと呼ぶ。
HTML構文の中では使えない。
v-bind

<div v-bind:id="dynamicId"></div>

上記例の様に、HTML属性の中で変数の展開が必要な場合に使う

{{ number + 1 }}

mustacheタグの中で、JavaScriptの式を動作させる。

左例だと、変数numberの値に1加算して返す。

v-bind

<div v-bind:id=""dynamicId""></div>

<a v-bind:href=""url""> ... </a>

上記の様に、HTML属性の中で変数の展開が必要な場合に使う

v-bindの省略記法 <a v-bind:href=""url""> ... </a>を以下のようにも書ける
<a :href=""url""> ... </a>
v-bind:class

クラスを動的に書き換える。
<div v-bind:class=""{ active: isActive }""></div>

だとisActiveがTrueの場合だけ、activeクラスが適用される。

v-onなどのイベント系や入力系ディレクティブは別の機会にまわします。 

 

Scriptタグ内のブロックの意味合いと役割

<script>タグ内でブロック的に定義し、それぞれのブロックには意味合いと役割が明確に決められてます。

種類はたくさんありますが、今回はまだ、初期表示しか行わないので、以下の3つだけを使います。

状態 意味と使いわけ
data

変数に値を定義します。

テンプレート内で利用する変数を単純に返します。

computed

関数を定義(例えば、test()みたいな)します。

その結果をテンプレート内で「test」として受け取ります

asyncData

コンポーネントをロードする前に非同期の処理を行います。

DOM構築前なので内部でthisは使えません。

 利用法のサンプルは、コード例に書くので割愛します。

確認用サンプルコード1

まずはルート[/」に対応する「index.vue」です。

  • 当日日付を取得してフォーマットして返す関数とその表示
  • リンクの名称を変数でわたすように変更

などをやってます。

サンプルのソースと補足を書きます。

pages/index.vue

<template>
  <div>
    <p>今日は{{ today }}です。</p>
    <img src="~assets/00_nuxt.JPG" alt="image01">
    <br>
    <ul>
      <li>
        <NLink to="/about">
          {{ linkname1 }}
        </NLink>
      </li>
      <li>
        <NLink to="/cityapi">
          {{ linkname2 }}
        </NLink>
      </li>
      <li>
        <a :href="outurl">
          {{ linkname3 }}
        </a>
      </li>
    </ul>
  </div>
</template>

<script>
import moment from 'moment'
export default {
  head: {
    title: 'First page'
  },
  data () {
    return {
      outurl: 'https://ja.nuxtjs.org/',
      linkname3: '(外部リンク)v-bindのサンプル',
      linkname2: '(内部リンク)市区町村サンプル',
      linkname1: '(内部リンク)郵便番号サンプル'
    }
  },
  computed: {
    today () {
      return moment().format('YYYY/MM/DD dddd')
    }
  }
}
</script>
  
当日日付を取得してフォーマットして返す関数とその表示

この部分です。

     today () {
      return moment().format('YYYY/MM/DD dddd')
    } 

ここでは、momentという日付処理ライブラリを使ってます。

これは標準ではないので、以下でインストールする必要があります。

npm install moment

 

関数の呼び出しと戻り値の表示

computed:内のJavaScriptの「today()」という関数名を、{{today}}と変数のように受けて戻り値を参照します。

<p>今日は{{ today }}です。</p>

注意点として、<templete>内で使う変数で、関数等の戻り値として初期値がセットされないものは、data()の中で初期値をセットして定義しておかないと、エラーになる点があります。 

 

確認用サンプルコード2

郵便番号APIから結果を受け取って表示します。

ステータスを見て正常(200)だったらレスポンスのJSONの内容を展開して表示して、以外だったらメッセージだけ表示するという感じの条件付き表示をやってます。

確認ポイントは。

  • v-ifを使った条件分岐(タグの表示・非表示)
  • JSONを受け取った時の内容表示の方法

です。

<template>
  <div>
    <NLink to="/">
      ルートのページへ戻ります
    </NLink>
    <br>
    <img src="/post.JPG" alt="image02">
    <div v-if="datas.status == 200">
      <p>郵便番号APIから応答がありました</p>
      <ul>
        <li>郵便番号:{{ datas.results[0].zipcode }}</li>
        <li>都道府県名:{{ datas.results[0].address1 }}</li>
        <li>市区町村名:{{ datas.results[0].address2 }}</li>
        <li>町域名:{{ datas.results[0].address3 }}</li>
      </ul>
    </div>
    <div v-else>
      <p>郵便番号APIからの応答ステータスがエラーになりました「 {{ datas.status }}」です。</p>
    </div>
  </div>
</template>

<script>
import axios from 'axios'
export default {
  asyncData () {
    return axios.get(`http://zipcloud.ibsnet.co.jp/api/search?zipcode=7830060&limit=1`)
      .then((res) => {
        return { datas: res.data }
      })
  },
  head: {
    title: 'About page'
  }
}
</script>

v-ifを使ったタグの表示・非表示とv-showとの使い分け

v-ifで条件に一致したものだけ表示します。

v-ifと同様に、条件によって表示・非表示を切り替えるディレクティブに「v-show」があって、ちょっと勘違いしやすいところがあるので補足します。

v-ifの場合は「初期表示において false の場合、何もしない」です。

なので、条件が最初に true になるまで描画自体されません(遅延描画)。

でも。

v-showの場合は、条件がTrueでもfalseでも描画自体は行われて、CSSのdisplayの切り替えで表示・非表示になるだけです。

なので、頻繁に表示・非表示を切り替える必要がある場合は、当然、v-showの方が速いとかがあるので、特徴を理解して使い分ける必要があります。

 

JSONを受け取った時の内容表示の方法

JSONが、こんな感じで入れ子になっているときは(一部割愛してますが)、datas.results[0].zipcodeのようにアクセスしないといけない・・というのが、意外と間違えやすい点です。

{
  "message": null,
  "results": [
      {
          "address1": "北海道",
          "address2": "美唄市",
          "address3": "上美唄町協和",
          "zipcode": "0790177"
      },
     {
          "address1": "北海道",
          "address2": "美唄市",
          "address3": "上美唄町南",
          "zipcode": "0790177"
      }
    ],
"status": 200
}

 

サンプルコード3

都道府県コードを指定して、市町村の一覧を取得して、それをテーブル展開します。

今回は簡単にするため、あえて、都道府県コードは固定にしています。

ここでのポイントは、

  • WEB-APIをリクエストする時にリクエストヘダーにAPI-KEYを埋め込んでいるところ。
  • v-forで複数データを繰り返し処理しているところ

です。

<template>
  <div>
    <NLink to="/">
      ルートのページへ戻ります
    </NLink>
    <br>
    <img src="/jamap.JPG" alt="image03">
    <div v-if="datas.message == null">
      <p>市区町村APIから応答がありました</p>
      <table>
        <tr>
          <th>都道府県コード</th>
          <th>市区町村コード</th>
          <th>市区町村名称(漢字名称)</th>
          <th>大都市識別フラグ</th>
        </tr>
        <tr v-for="data in datas.result" :key="data.id">
          <td>{{ data.prefCode }}</td>
          <td>{{ data.cityCode }}</td>
          <td>{{ data.cityName }}</td>
          <td>{{ data.bigCityFlag }}</td>
        </tr>
      </table>
    </div>
    <div v-else>
      <p>市区町村APIからの応答ステータスがエラーになりました「 {{ datas.message }}」です。</p>
    </div>
  </div>
</template>

<script>
import axios from 'axios'
export default {
  asyncData () {
    return axios
      .get(`https://opendata.resas-portal.go.jp/api/v1/cities?prefCode=1`, {
        headers: {
          'X-API-KEY': 'nomgXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
        },
        data: {}
      })
      .then((res) => {
        return { datas: res.data }
      })
  },
  head: {
    title: 'CityApi page'
  }
}
</script>

WEB-APIAPI-KEYを埋め込み方と空のdata()の注意

API-KEYみたいに、axiosでリクエストする時リクエストヘッダに何かを埋め込む場合にはheaders:で指定します。

  asyncData () {
    return axios
      .get(`https://opendata.resas-portal.go.jp/api/v1/cities?prefCode=1`, {
        headers: {
          'X-API-KEY': 'nomgXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
        },
        data: {}
      })
      .then((res) => {
        return { datas: res.data }
      })
  }, 

後ろの「data:{}」には注意してください。

一見空なのでいらなく見えるのですが、なぜか、これを削るとheadersの内容もきちんと反映しなくなったり不思議な挙動をします。

なので、必ずつけておく方が安全です。

 

v-forで繰り返し処理と配列の添え字に対する注意

APIのレスポンス(JSON)は内容データはネストしてはいってます。

1件だけアクセスするなら「datas.result[0].cityCode」でいいのですが、ここでのように「v-for」で繰り返し処理する場、この[0]の部分に考慮が必要です。

よくサンプルにのっている「 v-for="data in datas.result"」だけではエラーになります。

暗黙のidを「v-bind:key="data.id"」として補ってやらないといけません。

<tr v-for="data in datas.result" v-bind:key="data.id">

この「.id」というのが添え字を表しているのですが、APIのレスポンス仕様とかには書いていない暗黙の部分なので、最初ちょっととまどいます。

 

実行イメージ

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

あ・・もちろん、テスト専用ショートカットから起動したブラウザですよ。

f:id:arakan_no_boku:20190513225459p:plain

ふむ。

日付と曜日もしっかり表示されてますね。

郵便番号サンプルのリンクにいきます。

f:id:arakan_no_boku:20190513225618p:plain

おお、いい感じです。

次はルートに戻って、市区町村のリンクを押します。

f:id:arakan_no_boku:20190513225730p:plain

画面はそっけないですが・・まあ、それなりに動いてます。

参考:市区町村API

サンプルの中で、市区町村APIを使っています。

opendata.resas-portal.go.jp

こちらは郵便番号と違い事前にAPI-KEYを取得しておく必要があります。

f:id:arakan_no_boku:20190902203633p:plain

ではでは。