はじめに
input や button などの基本的な自作コンポーネントをさらにラップしたコンポーネントを使うときなどに、子コンポーネントのプロパティやイベントをすべて使いたい時がしばしばあります。そのようなときにどうすればいいのかをまとめました。
ルートの HTML 要素の属性は親から付与可能
そもそもの前提として、コンポーネントのルートの HTML 要素の属性は親から付与可能です。
下記のようなルート要素に input だけを配置した基本的なコンポーネントを作ったとします。
BaseInput.vue
<template>
<input type="text">
</template>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
@Component({})
export default class BaseInput extends Vue {}
</script>
<style scoped lang="scss"></style>
親コンポーネントから子コンポーネントのルート HTML 要素に属性を付与することができます。
CustomInput.vue
<template>
<base-input
class="base-input-class"
/>
</template>
…
子要素のプロパティを親からすべて利用可能にする
v-bind="$attrs"
を使うことで、ルート HTML 要素の属性以外の prop も親から利用可能になります。
BaseLabelInput.vue
<template>
<div>
<p>{{ label }}</p>
<input
type="text"
v-bind="$attrs"
>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator";
@Component({})
export default class BaseLabelInput extends Vue {
@Prop({ default: '' })
label!: string
}
</script>
CustomLabelInput.vue
<template>
<base-label-input
label="label name"
:value="test"
/>
</template>
ルート HTML 要素の属性を親から使用させたくないとき
ルート要素のプロパティを親に継承させたくないときは inheritAttrs: false
を使います。
下記の例では input をルート要素に持つ BaseInput コンポーネントに inheritAttrs: false
を適用した上で placeholder プロパティを付与しています。
BaseInput.vue
<template>
<input
type="text"
:placeholder="placeholder"
>
</template>
<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator";
@Component({ inheritAttrs: false })
export default class BaseInput extends Vue {
@Prop()
placeholder?: string
}
</script>
親コンポーネント側では value
属性を付与することはできません。
placeholder
prop は通常通り使用できます。
CustomInput.vue
<template>
<!-- value は無効、placeholder は有効 -->
<base-input
value="sample text"
placeholder="placeholder"
@input="onInput"
/>
</template>
...
すべてのイベントを親から使用したい時
プロパティと異なり、ルート要素であったとしても、子のイベントは受け取ることができません。
そこで、v-on=$listeners
を子要素に記述し、すべてのイベントを親コンポーネントで受け取れるようにします。
BaseInput.vue
<template>
<input
type="text"
v-on="$listeners"
>
</template>
...
CustomInput.vue
<template>
<base-input
@input="onInput"
@click="onClick"
@focus="onFocus"
>
</template>
...