【Vue.js】コンポーネントを使うとうまく表示されない時の解決策


はじめに

Vue.jsを学習している最中にコンポーネントの使い方について躓いたところがあったので、
記録かつ同じ所で躓いた方のためになればと思い、投稿します。

コンポーネントとは?

公式サイト引用

Vue においては、「コンポーネント」は本質的にはあらかじめ定義されたオプションを持つ Vue インスタンスです。

簡単に言い換えると独自のHTMLタグを再利用したいときに使用するものですかね。

使ってみる

jsファイルに"user-item"というコンポーネントを作成。

sample.js

Vue.component('user-item', {
  props: ['user']
  template: '<li>{{ user.name }}</li>'
})

new Vue({
  el: "app-user"
  data: {
    users: [
      {id: 001, name: "Sato"},
      {id: 002, name: "Tanaka"},
      {id: 003, name: "Suzuki"},
    ]
  } 

HTML側で"user-item"タグを使用する。

sample.html
    <div id="app-user">
      <ol>
        <user-item
          v-for="user in users"
          v-bind:user="user"
          v-bind:key="user.id"
        ></user-item>
      </ol>
    </div>

画面での表示はこうなります。
1. Sato
2. Tanaka
3. Suzuki

躓いたポイント

上記のようにV-forを使ってテーブルでも同じことがしたいと思い試したところ、うまくいきませんでした。
試したコード

table.js

Vue.component('user-table', {
  props: ['user'],
  template: '\
    <tr>\
      <td>{{ user.id }}</td>\
      <td>{{ user.name }}</td>\
    </tr>\
    '
})

new Vue({
  el: "#app-table",
  data: {
    users: [
      {id: 001, name: "Sato"},
      {id: 002, name: "Tanaka"},
      {id: 003, name: "Suzuki"},
    ]
  }
});
table.html

    <div id="app-table">
      <table>
        <thead>
          <tr>
            <th>ID</th>
            <th>NAME</th>
          </tr>
        </thead>
        <tbody>
          <user-table
            v-for="user in users"
            v-bind:user="user"
            v-bind:key="user.id"
          ></user-table>
        </tbody>
      </table>
    </div>

画面での表示
1Sato
2Tanaka
3Suzuki
ID NAME

ヘッダーが一番最後にきてしまっています。

解決策

HTMLにはタグの入れ子にルールがあり、なんでも入れれるわけではありません。
tbodyタグに関してMDN調べると、

許可されている内容 : 0 個以上の tr 要素

つまり、今回のuser-tableタグはHTMLのルールに引っかかっていることが分かりました。

そんな場合にVueではis属性というものが提供されています。
is属性を使用したHTMLコード

table.html(修正後)

    <div id="app-table">
      <table>
        <thead>
          <tr>
            <th>ID</th>
            <th>NAME</th>
          </tr>
        </thead>
        <tbody>
          <tr is="user-table"
            v-for="user in users"
            v-bind:user="user"
            v-bind:key="user.id"
          ></tr>
        </tbody>
      </table>
    </div>

画面での表示

うまく表示できました。

まとめ

HTMLの入れ子タグにはルールがあるので、is属性を使って回避しました。