1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Vue3で子エレメントの外側を別のエレメントでラップする方法2 - 子エレメントが`v-for`で動的に渡される場合

Last updated at Posted at 2023-12-07

導入

今日の話題は昨日の続きで子エレメントがv-forで動的に渡される場合にその子エレメントの外側を別のエレメントでラップする方法についてです。

※ 私が株式会社愛宕 Advent Calendar 2023に書く記事は主に社内向けに共有しておきたいけど勉強会をするまでもないちょっとしたTipsにしたいと思います。

もう一度、目的の構造を示します。

<div class="group">
  <div class="group-item">
    <div>hoge</div>
  </div>
  <div class="group-item">
    <div>fuga</div>
  </div>
  <div class="group-item">
    <div>piyo</div>
  </div>
</div>

そして、次のようにExampleGroupを使用して目的の構造が出力されるようにする方法は昨日投稿した通りです。

app.ts
<template>
  <ExampleGroup>
    <div>hoge</div>
    <div>fuga</div>
    <div>piyo</div>
  </ExampleGroup>
</template>

今回は、こんな感じの場合どうしたらいいかという話です。

こうとか
<template>
  <ExampleGroup>
    <div v-for="item in items">{{ item.name }}</div>
  </ExampleGroup>
</template>
こんなんとか
<template>
  <ExampleGroup>
    <div>hoge</div>
    <div v-for="item in items">{{ item.name }}</div>
  </ExampleGroup>
</template>

解決

$slots.default()から直接vnodeを取得せず一旦必要なvnodeだけを取り出し、それをもとに<component>で描画するようにします。

<div v-for="item in items">{{ item.name }}</div>

で作成したエレメントはvnodeそのものではなくvnode.childrenに含まれているのがキモです。

ExampleGroup.vue
<script setup lang="ts">
// SFCでComposition APIを使うためにscript setupを使って書いています

import { isVNode } from 'vue'

// template内で書くときの$slotsと同じ
const slots = useSlots()

const vnodes = computed(() => {
  if (!slots.default) {
    // 念の為、slotが空の場合があってもいいように
    return []
  }

  const result = []
  
  for (let vnode of slots.default()) {
    if (Array.isArray(vnode.children)) {
      // template内でv-forでループさせた各エレメントはvnode.childrenに入っている
      for (let child of vnode.children) {
        if (isVNode(child)) {
          // 念の為、vnodeがどうか確認してから描画対象に追加する
          result.push(child)
        }
      }
    } else {
      // vnodeにchildrenがない場合は普通のvnodeなのでそのまま追加
      result.push(vnode)
    }
  }
  
  return result
})

</script>

<template>
  <div class="group">
    <div class="group-item" v-for="vnode in vnodes">
      <component :is="vnode"></component>
    </div>
  </div>
</template>
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?