🌵
動的コンポーネントで@clickが動かないときは@click.nativeを使う
業務でNuxtを使っていて、うまく@click
が発火しない現象に遭遇したのでメモ。
環境
- macOS Big Sur 11.5.2
- nuxt@2.15.2
やっていることとしては、動的コンポーネントを利用して、
-
link
プロパティが渡されていればa
タグとして出力する -
link
プロパティが渡されていなければbutton
タグとして出力する
というものです。buttonとして使いたいときはメソッドを呼び出して処理を行わせたいのですが、@click
を設定してもうまく発火してくれません。
<!--@clickだとsampleメソッドが呼び出されない-->
<dynamic-component-sample
@click="sample"
>Button</dynamic-component-sample>
※dynamic-component-sampel
は以下のようなコンポーネントを定義しています。
DynamicComponentSample.vue
<template>
<div class="border-2 bg-gray-200 m-3 p-3">
<component
:is="type"
:href="link"
>
<slot />
</component>
</div>
</template>
<script>
export default {
props: {
link: String,
default: null
},
computed: {
type(){
if(this.link){
return 'a';
} else {
return 'button'
}
}
}
}
</script>
対応策
結論としては、.native
修飾子を使うことで解決できました。
<!--@clickではなく、@click.nativeにする-->
<dynamic-component-sample
@click.native="sample"
>Button</dynamic-component-sample>
.native
ってなに?
Vue.jsの公式ドキュメントを漁ってみると、以下のような説明がありました。
.native - listen for a native event on the root element of component.
コンポーネントのルート要素上のネイティブイベントをリッスンするといったもののようです。
通常のbutton
などのネイティブなHTML要素でイベントハンドリングしたいときはv-on
ディレクティブを使えばいいですが、
自分で作成したコンポーネントでa
やbutton
、input
などのネイティブイベントを使いたい場合はさらに.native
修飾子をつける必要があります。
なお、公式の以下のページだと、コンポーネント定義時に$listeners
プロパティを使って、button
やinput
と同じように.native
を使わずにすむ
コンポーネントの作成方法が記載されていますので、そうしたい方には参考になりそうです。
ソース全体
DynamicComponentSample.vue
<template>
<div class="border-2 bg-gray-200 m-3 p-3">
<component
:is="type"
:href="link"
>
<slot />
</component>
</div>
</template>
<script>
export default {
props: {
link: String,
default: null
},
computed: {
type(){
if(this.link){
return 'a';
} else {
return 'button'
}
}
}
}
</script>
index.vue
<template>
<div class="container">
<dynamic-component-sample
:link="'https://google.com'"
>aタグ</dynamic-component-sample>
<br>
<!--@clickではなく、@click.nativeにする-->
<dynamic-component-sample
@click="sample"
>Button</dynamic-component-sample>
</div>
</template>
<script>
import DynamicComponentSample from '~/components/DynamicComponentSample.vue';
export default {
components: { DynamicComponentSample },
data() {
return { test: process.env.NODE_ENV };
},
methods: {
sample(){
window.alert('ボタンタグを押したよ')
}
}
};
</script>
Discussion