はじめに
たとえば以下のようなコンポーネントがあったとします。
<template>
<input type="text" class="form-control">
</template>
今回はこのコンポーネントを利用する親コンポーネントから、コンポーネント内部の input
タグへの参照を取得することを目指します。
親からフォーカスを当てたい時などに使えると思います。
Vue のバージョンは3系を前提とし、 Composition API を利用します。
親コンポーネント
まず親からどうやって使うかを考えてみます。
単純に子コンポーネントを ref
で参照すると、残念ながらDOMではなくコンポーネントインスタンスへの参照になってしまいます。
<template>
<input-component ref="inputRef" />
</template>
<script lang="ts">
import { defineComponent, ref, onMounted } from 'vue'
import InputComponent from './components/InputComponent.vue'
export default defineComponent({
components: {
InputComponent,
},
setup() {
const inputRef = ref<HTMLInputElement | undefined>()
onMounted(() => {
// HTMLInputElement ではなくコンポーネントインスタンスが取得される
// 当然 focus メソッドなど存在しない
console.log(inputRef.value)
})
return {
inputRef
}
}
})
</script>
では子コンポーネントからどうやってデータ(DOM)を上げるかというと、 v-model
(= emit
)を利用します。
2系の場合は sync 修飾子 です。
<template>
<input-component v-model:inputRef="inputRef" />
</template>
これで子コンポーネントからDOMを取得する準備ができました。
子コンポーネント
子コンポーネントでは、 ref
で取得したDOMの参照を emit
を通して上げるだけです。
<template>
<input type="text" class="form-control" ref="ref">
</template>
<script lang="ts">
import { defineComponent, computed } from 'vue'
export default defineComponent({
props: {
inputRef: {
type: HTMLInputElement,
},
},
setup(props, { emit }) {
const ref = computed<HTMLInputElement | undefined>({
get() {
return props.inputRef
},
set(value) {
emit('update:inputRef', value)
}
})
return {
ref
}
}
})
</script>
ref
に WritableComputed
を渡すのがポイントです。 set
で emit()
します。
おわりに
親から子コンポーネント内部のDOMの参照を取得できるようになりました。
id
を渡して getElementById
を呼んだり、または子コンポーネントに focus
メソッドを用意してコンポーネントインスタンスを直接参照する方法などもありますが、個人的にはこちらのほうがすっきりしてていいと思います。
Atomic Design などで設計する時の参考になれば。