今回は条件付きレンダリング(v-if)やリストレンダリング(v-for)および変数参照方法などのVue.jsベースの基本的なところを確認していきます。
この記事は「NUXT自己流チュートリアル(1)」の2回目です。
1回目から続けて読んでもらえることを想定して書いていますのでご了承ください。
事前に頭にいれといた方が良いこと
Nuxt.jsは「Vue.jsアプリケーションを開発するためのフレームワーク」なので、当然、Vue.jsのディレクティブ(v-で始まり、HTMLタグ内で使う特別な属性)やテンプレート構文(”Mustache” 構文(二重中括弧{{}})で変数を参照するなど)を使います。
なので。
今回は大部分が「Vue.js」に関連する内容になります。
今回の記事の前提的なこと
自己流チュートリアル(1)の続きなので、その記事で「前提」と「チュートリアルの準備」として書いていることは、できている必要があります。
つまり。
インストール・環境設定・テスト専用Chromeショートカットの作成、自前の画像ファイルの準備・・うんぬんですね。
まだの方は、下記記事を先に参照して、環境設定をお願いします。
あと、サンプルの中で、市区町村APIを使っています。
こちらは郵便番号と違い事前にAPI-KEYを取得しておく必要があります。
こちらからメールアドレスを登録して、API-KEYを取得してださい。
今回の目的
vue.jsの以下の機能を使ってみたいと考えてます。
テンプレート内で利用する機能
条件付きレンダリング
ディレクティブ | ふるまい |
---|---|
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 |
クラスを動的に書き換える。 だとisActiveがTrueの場合だけ、activeクラスが適用される。 |
v-onなどのイベント系や入力系ディレクティブは別の機会にまわします。
Scriptタグ内で使う機能
<script>タグ内でブロック的に定義し、それぞれのブロックには意味合いと役割が明確に決められてます。
種類はたくさんありますが、今回はまだ、初期表示しか行わないので、以下の3つだけを使います。
状態 | 意味と使いわけ |
---|---|
data |
変数に値を定義します。 テンプレート内で利用する変数を単純に返します。 |
computed |
関数を定義(例えば、test()みたいな)します。 その結果をテンプレート内で「test」として受け取ります |
asyncData |
コンポーネントをロードする前に非同期の処理を行います。 DOM構築前なので内部でthisは使えません。 |
利用法のサンプルは、コード例に書くので割愛します。
さて、サンプルを作ってみよう
今回も画面の体裁(CSS)はほぼ無視です。
やることは。
- 郵便番号APIを使って、正常(200)の場合だけ、情報を表示する。
- 市区町村APIを使って、取得した市区町村をテーブルにリストする。
- 変数を使ってURLを渡して外部リンクを機能させる。
- システム日付を取得して今日の日付を表示してみる。
です。
この中で、上記にあげた各ディレクティブや状態・テンプレート構文などを使います。
なお、JavaScriptの関数例の中で、日付を扱う予定です。
JavaScriptのデフォルトだと少々ごちゃごちゃするので、すっきりさせるため、momentという日付処理ライブラリもインストールしてます。
いれてない場合は、以下でインストールしておいてください。
npm install moment
layouts
共通レイアウトは、自己流チュートリアル(1)のままです。
こんな感じの超シンプルなものをそのまま使います。
layouts/default.vue
<template> <div> <nuxt /> </div> </template>
最低限の基本系です。
pages/index.vue
まずはルート[/」に対応する「index.vue」です。
自己流チュートリアル(1)のソースを変更して。
- 当日日付を取得してフォーマットして返す関数とその表示
- リンクの名称を変数でわたすように変更
などをやってます。
あと、画像ファイルを追加します。
以下の画像を「(プロジェクトフォルダ)\assets\00_nuxt.JPG」で保存しておきます。
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>
ここでのポイントは以下です。
- mustache 構文「{{}}」で囲ってHTMLタグ外での変数の参照をする点
- HTMLタグ内では「{{}}」が使えず、b-bindを使って変数の参照をする点
- 内部リンクと異なり外部リンクは普通に<a></a>をつかう必要があること
- data()内で使う変数を定義して初期値のセットをすること
- computed:内でのJavascriptの実行して値を返して、変数として受け取ること
です。
最後だけ補足すると、computed:内のJavaScriptの「today()」という関数名を、{{today}}と変数のように受けて戻り値を参照します。
あと、<templete>内で使う変数で、関数等の戻り値として初期値がセットされないものは、data()の中で初期値をセットして定義しておかないと、エラーになります。
pages/about.vue
郵便番号APIから結果を受け取って表示します。
自己流チュートリアル(1)のソースでは、ステータスだけを表示してましたが、今回はそれに手を加えて、ステータスを見て正常(200)だったらレスポンスの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を使った条件分岐(タグの表示・非表示)
- JSONを受け取った時の内容表示の方法
です。
v-ifを使った条件分岐(タグの表示・非表示)
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
}
pages/cityapi.vue
今回新規に付け加えるソースです。
都道府県コードを指定して、市町村の一覧を取得して、それをテーブル展開します。
準備として、画像をひとつ追加しておきます。
以下の画像を「(プロジェ区とフォルダ)\static\jamap.JPG」で保存しておきます。
今回は簡単にするため、あえて、都道府県コードは固定にしています。
<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-APIをリクエストする時にリクエストヘダーにAPI-KEYを埋め込んでいるところ。
API-KEYみたいに、axiosでリクエストする時リクエストヘッダに何かを埋め込む場合にheaders:で指定するやり方自体は、上記の型を覚えれば難しくはないです。
でも、その後ろの「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」にアクセスします。
あ・・もちろん、テスト専用ショートカットから起動したブラウザですよ。
ふむ。
日付と曜日もしっかり表示されてますね。
郵便番号サンプルのリンクにいきます。
おお、いい感じです。
次はルートに戻って、市区町村のリンクを押します。
画面はそっけないですが・・まあ、それなりに動いてます。
まずまずですね。
今回は、このくらいで・・。
ではでは。