簡単にできそうで、意外ともどかしい。htmlタグを含んだdataを、Vue.jsでhtmlとして表示させる方法
Vue.jsを用いて、WebアプリやWebサイトを制作していると仮定します。
その際、コンテンツとして使いたい data が、たとえば次のような内容だったとします。
new Vue({ el: '#app', vuetify: new Vuetify(), data: { mytitle: 'Vue.jsをつかってみる' mycontent: '<span class="myclass">この章で<strong>重要</strong>なのは</span>’, }, mounted() { ... (中略) ... });
↑サンプルとして、単純にタイトルと本文をdataとして受け取るケースを想定しています。(実際はもうすこし複雑にdataが並ぶかと思いますが)
WebアプリやWebサイトのコンテンツ自体を、firebase の firestore であったり、何らかのデータベースから取得している場合。
特に文章を取得している場合などによく起こりうる話かと思うのですが、data内にhtmlタグが含まれていて、
かつ、それをテキストデータ(string)ではなく、htmlはhtmlとして表示させたいケース
があったりします。
今回の記事は、それを簡単に実現する方法について、書いていきたいと思います。
通常のマスタッシュ構文では、htmlとして解釈されない
マスタッシュ構文とは、
{{ mycontent }}
←これのことです。
Vue.jsのコンポーネント内で、なんらかのdataを展開しようとする場合、
一番単純に書ける方法がこのマスタッシュ構文で、よく使われていることと思います。
ただし、冒頭の mycontent のような、htmlタグを含む data をそのままマスタッシュ構文で展開すると、
表示される結果は string (テキストデータ)となり、htmlタグが解釈されず、そのままの状態で文字として表示されてしまいます。
<h2>{{ mytitle }}</h2> <p>{{ mycontent }}</p>
↑一見、これで正常に表示されるように見えますが、実際はhtmlが解釈されず、htmlタグもテキストとして表示されてしまいます。
コンテンツの準備もできて、Webアプリ自体の挙動も正常になり、あとは両者をつなぐだけなのに、
いざコンテンツを表示する段階になってこういう現象に出くわすと、
「なぜこんな単純なことが簡単にできないんだ…?」
という、もどかしさが募りますよね。
ではどうするかと言うと、そこはさすが Vue.js。
こういう時に使えるものが、ちゃんと用意されていました。
v-html を使ってdataをhtmlとして表示する
v-html
。
そうです。v-html
こそ、この問題を解決してくれるVue.jsのディレクティブです。
マスタッシュ構文の代わりに、これをどのように記述すれば良いかと言うと次のようになります。
<h2>{{ mytitle }}</h2> <p v-html="mycontent"></p>
↑htmlタグを含んでいるdataを、v-htmlと紐づけるだけです。
何のことはなく、これで問題なく、dataをhtmlとして表示させることが可能になります。
これを知らなかった当初は、
「data をいかにして parse すればいいのか…一回変数で受け取って、jsで処理する? でも、仮にこれ、parseできたとしても、それをどう差し込むの?」
などと無駄に考える時間を使ってしまいましたが、v-html
。これのおかげで一発で解決しました。
v-htmlにはjavascriptの記法で値を渡しても動作する
「data内に含まれるhtmlタグがテキストとして表示されてしまう問題」は、v-html
によって無事解決したように思われました。
…が、私が実際のWeb制作で出くわしたケースは、
ここまでのセクションで書いてきた単純な使い方ではなく、
「v-for の中で、htmlを含んでいるdataを一つずつ取り出して展開する
」
と言うものでした。
しかし、そこはやはり Vue.js。柔軟に対応してくれました。
このv-html
、v-for
の中でもしっかり動作して、
かつdata名を紐づける形ではなく、javascript的な書き方で値を渡しても、ちゃんと動いてくれました。
どういうことかと言うと、
たとえば、次のような連番を持っているdataを、v-for
で展開したい場合があるとします。
new Vue({ el: '#app', vuetify: new Vuetify(), data: { posts: [ { mytitle1: 'Vue.jsをつかってみる', mycontent1: '双方向の<strong>data binding</strong>’ }, { mytitle2: 'WordPressをつかってみる', mycontent2: 'この<strong>WordPress</strong>というのは’ }, { mytitle3: 'Vuetifyをつかってみる', mycontent3: 'とても重宝する<strong>UIフレームワーク</strong>’ }, { mytitle4: 'Swiperをつかってみる', mycontent4: '<strong>Vanilla JS</strong>のみで動作するスライダー’ }, { mytitle5: 'Gatsby.jsをつかってみる', mycontent5: '<strong>静的サイトジェネレーター</strong>としての’ }, ] }, mounted() { ... (中略) ... } });
↑posts というdataの中に、mytitle+連番、 mycontent+連番、の形でコンテンツが入っている場合を想定しています。
上記のようなhtmlタグが含まれているdataを v-for
でhtmlとして展開して表示したい場合でも、
次のように書くだけで、 v-html
はしっかり動作してくれました。
<template v-for="n in posts" :key="n"> <h2> {{ mytitle + `${n}` }} </h2> <p v-html="mycontent + `${n}`"> </p> </template>
↑このように、v-html
は v-for
の中でもしっかり動作してくれます。
`${n}` の n には、 v-for
の繰り返し回数が代入されるので、
mytitle1
mycontent1
mytitle2
mycontent2
…
のように、順番にdataの内容を取り出して表示することができる、と言うわけです。
(なお、この例はv-forが一階層のみですが、実制作ではv-forが何重かにネストされているケースもあると思います。実際に試してみたのですが、ネストされた v-for 内でも、全く問題なく v-html は動作
してくれました。)
v-htmlの公式解説
Vue.js公式サイトの、v-htmlが解説されているページは以下になります。一読することをおすすめします。
テンプレート構文 — Vue.js
この記事のまとめ
今回は、Vue.jsでWebアプリやWebサイトを制作している際に、
「data内にhtmlタグを含んでいるコンテンツを、テキストではなく、htmlとして表示したい」
といったケースで使える
v-html
について、書きました。
個人的にとても感動したのが、この記事末尾でご紹介しているように、
「data単体だけではなく、javascriptの記法で値を渡しても、ちゃんと動作してくれる」
「v-for
の中でも普通に動作してくれる」
と言う点でした。
コンテンツとして使いたいdataに、htmlタグが含まれていることは実制作で割とあるケースだと思うので、
この v-html
ディレクティブ、とても使い勝手が良く助かっています。
当サイトでは、今後も Vue.js に関する記事を掲載していく予定です。
※この記事は、内容の品質向上を目的に、随時更新を行う場合があります。