Vue v-on:clickがコンポーネントで機能しない


186

コンポーネント内でon clickディレクティブを使用しようとしていますが、機能しないようです。コンポーネントをクリックしても、コンソールで「テストクリック」が行われるはずなのに何も起こりません。コンソールにエラーが表示されないので、何が問題なのかわかりません。

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>vuetest</title>
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

App.vue

<template>
  <div id="app">
    <test v-on:click="testFunction"></test>
  </div>
</template>

<script>
import Test from './components/Test'

export default {
  name: 'app',
  methods: {
    testFunction: function (event) {
      console.log('test clicked')
    }
  },
  components: {
    Test
  }
}
</script>

Test.vue(コンポーネント)

<template>
  <div>
    click here
  </div>
</template>

<script>
export default {
  name: 'test',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  }
}
</script>

回答:


331

コンポーネントのルート要素でネイティブイベントをリッスンする場合は、次のように.native修飾子を使用する必要がありv-onます。

<template>
  <div id="app">
    <test v-on:click.native="testFunction"></test>
  </div>
</template>

または短縮形で、コメントで示唆されているように、次のこともできます:

<template>
  <div id="app">
    <test @click.native="testFunction"></test>
  </div>
</template>

3
または略記@click.native="testFunction"
桟橋

75
ネイティブイベントとは何ですか、または他の通常のイベントとどのように異なりますか?なぜルート要素のこの特別なケース???
MrClan 2017


9
ネイティブ修飾子はvueで使用しないことをお勧めします。どうしても必要な場合にのみ使用してください。これを実現する正しい方法については@ jim-mcneelyを参照してください。つまり、子要素からイベントを発行してから、親でそれを復活させます。
Deiknymi


32

私は$emitあなたが求めていると私が思うことに対して機能がよりうまく機能すると思います。コンポーネントをVueインスタンスから分離したままにして、多くのコンテキストで再利用できるようにします。

// Child component
<template>
  <div id="app">
    <test @click="$emit('test-click')"></test>
  </div>
</template>

HTMLで使用する

// Parent component
<test @test-click="testFunction">

5
これが正解だと思います。イベントのリンクをコンポーネント内で処理します。呼び出すクリックイベントの「バージョン」の親コンポーネントは関係ありません。私は実際にそれをコンポーネントの以下の答えとして実装し@click="$emit('click')"、そのようにして親コンポーネントは通常の@click
Nelson Rodriguezを

2
私は少し混乱しています。$ emitパーツはおそらくテストコンポーネントテンプレートにあるはずですか?
Stefan Fabian、

1
@NelsonRodriguezが言ったこと。を使用し@click="$emit('click')"ます。
Nifel

20

それはだ@Neps'と答えたが、詳細に。


@Saurabhの回答は、コンポーネントを変更したくない場合やコンポーネントにアクセスできない場合に適しています。


@clickが機能しないのはなぜですか?

コンポーネントは複雑です。1つのコンポーネントは小さなファンシーボタンラッパーで、もう1つのコンポーネントは内部に多数のロジックを持つテーブル全体です。Vueは、バインドv-modelまたは使用するときにユーザーが何を期待しているのか正確に把握していないv-onため、コンポーネントの作成者がすべて処理する必要があります。

クリックイベントの処理方法

Vue docsによると、$emitイベントを親に渡します。ドキュメントの例:

メインファイル

<blog-post
  @enlarge-text="onEnlargeText"
/>

成分

<button @click="$emit('enlarge-text')">
  Enlarge text
</button>

@v-on 省略表記です

コンポーネントはネイティブclickイベントを処理し、親のイベントを発行します@enlarge-text="..."

enlarge-textに置き換えてclick、ネイティブクリックイベントを処理しているように見せることができます。

<blog-post
  @click="onEnlargeText"
></blog-post>
<button @click="$emit('click')">
  Enlarge text
</button>

しかし、それだけではありません。$emitイベントで特定の値を渡すことができます。nativeの場合click、値はMouseEventです。(Vueとは何の関係もないJSイベント)です。

Vueはそのイベントを$event変数に格納します。したがって、$eventネイティブイベントの使用法の印象を作成するために、イベントで発行するのが最善です。

<button v-on:click="$emit('click', $event)">
  Enlarge text
</button>

8

少し冗長ですが、これは私がそれを行う方法です:

@click="$emit('click', $event)"


8
これはどこに行くの?なぜそこに置くのですか?この回答を表示している人のためにもう少し詳細を追加してください。
mix3d 2018

この例では、これはTest.vueコンポーネントのdivタグに配置されます。その後、App.vueでコンポーネントテストを使用するときにv-on:click = "testFunction"または@ click = "testFunction"を使用できます
Tim Wickstrom

に変更しました$emitが、何も起こりません。他に何かする必要があり$emitますか? jsfiddle.net/xwvhy6a3
Richard Barraclough

@RichardBarracloughコンポーネントがカスタムイベント「clickTreeItem」を発行するようになりました。次に、そのコンポーネントの使用時にそのイベントをどう処理するかを処理します。v-on:myEvent = "myMethod"
Neps

5

コンポーネントのネイティブイベントは、親要素から直接アクセスできません。代わりにを試すv-on:click.native="testFunction"か、Testコンポーネントからイベントを発行することもできます。のようにv-on:click="$emit('click')"


4

VueCONF US 2019の Chris Fritz(Vue.js Core Team Emeriti)が述べたように

Kiaを.native入力してから、ベース入力のルート要素が入力からラベルに変更された場合、このコンポーネントは突然壊れ、それは明白ではありません。実際、本当に良いテストがない限り、すぐにキャッチすることすらできません。代わりに、現在アンチパターンがVue 3で削除されると私が考えている.native修飾子の使用を回避することにより、親がどの要素リスナーが追加されるかを気にするかもしれないことを明示的に定義することができます...

Vue 2を使用

使用$listeners

したがって、Vue 2を使用している場合、この問題を解決するには、完全に透過的なラッパーロジックを使用することをお勧めします。このため、Vueは$listenersコンポーネントで使用されているリスナーのオブジェクトを含むプロパティを提供します。例えば:

{
  focus: function (event) { /* ... */ }
  input: function (value) { /* ... */ },
}

その後、次のようにコンポーネントに追加v-on="$listeners"する必要がありtestます。

Test.vue(子コンポーネント)

<template>
  <div v-on="$listeners">
    click here
  </div>
</template>

これで<test>コンポーネントは完全に透明なラッパーになりました。つまり、通常の<div>要素とまったく同じように使用できます。すべてのリスナーは.native修飾子なしで機能します。

デモ:

Vue.component('test', {
  template: `
    <div class="child" v-on="$listeners">
      Click here
    </div>`
})

new Vue({
  el: "#myApp",
  data: {},
  methods: {
    testFunction: function(event) {
      console.log('test clicked')
    }
  }
})
div.child{border:5px dotted orange; padding:20px;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="myApp">
  <test @click="testFunction"></test>
</div>

$emitメソッドの使用:

$emitこの目的でメソッドを使用することもできます。これは、親コンポーネントの子コンポーネントイベントをリッスンするのに役立ちます。このため、最初に次のように子コンポーネントからカスタムイベントを発行する必要があります。

Test.vue(子コンポーネント)

<test @click="$emit('my-event')"></test>

重要:イベント名には常にケバブケースを使用してください。この点に関する詳細とデモについては、この回答を確認してください:VueJSが計算された値をコンポーネントから親に渡します

ここで、次のように、親コンポーネントでこの発行されたカスタムイベントをリッスンする必要があります。

App.vue

<test @my-event="testFunction"></test>

だから、基本的には代わりのv-on:clickか、速記@click、我々は単に使用するv-on:my-eventか、単に@my-event

デモ:

Vue.component('test', {
  template: `
    <div class="child" @click="$emit('my-event')">
      Click here
    </div>`
})

new Vue({
  el: "#myApp",
  data: {},
  methods: {
    testFunction: function(event) {
      console.log('test clicked')
    }
  }
})
div.child{border:5px dotted orange; padding:20px;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="myApp">
  <test @my-event="testFunction"></test>
</div>


Vue 3を使用

使用v-bind="$attrs"

Vue 3は、多くの点で私たちの生活をはるかに簡単にします。その例の1つは、を使用するだけで、非常に少ない構成でシンプルな透過ラッパーを作成するのに役立つことv-bind="$attrs"です。これを子コンポーネントで使用すると、リスナーが親から直接機能するだけでなく、他の属性も通常と同じように機能します<div>

したがって、この質問に関しては、Vue 3で何も更新する必要はありません。<div>ここでのルート要素と同様に、コードは引き続き正常に機能し、すべての子イベントを自動的にリッスンします。

デモ#1:

const { createApp } = Vue;

const Test = {
  template: `
    <div class="child">
      Click here
    </div>`
};

const App = {
  components: { Test },
  setup() {
    const testFunction = event => {
      console.log("test clicked");
    };
    return { testFunction };
  }
};

createApp(App).mount("#myApp");
div.child{border:5px dotted orange; padding:20px;}
<script src="//unpkg.com/vue@next"></script>
<div id="myApp">
  <test v-on:click="testFunction"></test>
</div>

しかし<input />、親ラベルの代わりに属性とイベントをメインに適用する必要があるネストされた要素を持つ複雑なコンポーネントの場合、単純に次のように使用できますv-bind="$attrs"

デモ#2:

const { createApp } = Vue;

const BaseInput = {
  props: ['label', 'value'],
  template: `
    <label>
      {{ label }}
      <input v-bind="$attrs">
    </label>`
};

const App = {
  components: { BaseInput },
  setup() {
    const search = event => {
      console.clear();
      console.log("Searching...", event.target.value);
    };
    return { search };
  }
};

createApp(App).mount("#myApp");
input{padding:8px;}
<script src="//unpkg.com/vue@next"></script>
<div id="myApp">
  <base-input 
    label="Search: "
    placeholder="Search"
    @keyup="search">
  </base-input><br/>
</div>


1
これは受け入れられる答えになるはずです。ありがとう!
アリジュニオール

0

ドキュメントから:

JavaScriptの制限により、Vueは配列に対する以下の変更を検出できません。

  1. インデックスを使用してアイテムを直接設定する場合(例:vm.items [indexOfItem] = newValue)
  2. 配列の長さを変更する場合(例:vm.items.length = newLength)

私の場合、AngularからVUEに移行するときにこの問題に遭遇しました。修正は非常に簡単でしたが、見つけるのは本当に困難でした。

setValue(index) {
    Vue.set(this.arr, index, !this.arr[index]);
    this.$forceUpdate(); // Needed to force view rerendering
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.