Vue v-on: klik tidak berfungsi pada komponen

188

Saya mencoba menggunakan arahan klik di dalam komponen tetapi sepertinya tidak berhasil. Ketika saya mengklik komponen tak berguna terjadi ketika saya harus mendapatkan 'tes diklik' di konsol. Saya tidak melihat kesalahan di konsol, jadi saya tidak tahu apa yang saya lakukan salah.

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>

Aplikasi

<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 (komponen)

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

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

Jawaban:

335

Jika Anda ingin mendengarkan acara asli pada elemen akar komponen, Anda harus menggunakan .native pengubah untuk v-on, seperti berikut:

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

atau singkat, seperti yang disarankan dalam komentar, Anda juga dapat melakukan:

<template>
  <div id="app">
    <test @click.native="testFunction"></test>
  </div>
</template>
Saurabh
sumber
4
Atau steno@click.native="testFunction"
Dermaga
76
apa acara asli atau bagaimana bedanya dengan acara normal lainnya? Mengapa ini kasus khusus untuk elemen root ???
MrClan
24
@MrClan vuejs.org/v2/guide/…
user2875289
9
pengubah asli disarankan agar tidak digunakan dalam vue. Gunakan hanya jika benar-benar diperlukan. Lihat @ jim-mcneely untuk cara yang benar untuk mencapai ini yaitu memancarkan peristiwa dari elemen anak dan kemudian menghidupkannya kembali di induk.
Deiknymi
5
Berikut tautan yang benar ke acara asli
Alexander Kim
32

Saya pikir $emitfungsinya bekerja lebih baik untuk apa yang saya pikir Anda minta. Itu membuat komponen Anda terpisah dari instance Vue sehingga dapat digunakan kembali dalam banyak konteks.

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

Gunakan itu dalam HTML

// Parent component
<test @test-click="testFunction">
Jim McNeely
sumber
5
Saya yakin ini jawaban yang benar. Tangani di dalam komponen tautan dari acara tersebut. Jangan khawatirkan komponen induk dari "versi" acara klik mana yang harus dihubungi. Saya benar-benar menerapkannya sebagai jawaban di bawah dalam komponen @click="$emit('click')"dan dengan cara itu komponen induk hanya menggunakan yang biasa@click
Nelson Rodriguez
2
Saya agak bingung. Apakah bagian $ emit seharusnya ada di templat komponen pengujian?
Stefan Fabian
1
Apa yang dikatakan @NelsonRodriguez. Gunakan @click="$emit('click')".
Nifel
20

Ini jawaban @Neps tetapi dengan detail.


Catatan : Jawaban Saurabh lebih cocok jika Anda tidak ingin mengubah komponen Anda atau tidak memiliki akses ke sana.


Mengapa @klik tidak bisa berfungsi?

Komponennya rumit. Satu komponen bisa menjadi pembungkus tombol mewah kecil, dan komponen lain bisa menjadi seluruh tabel dengan sekelompok logika di dalamnya. Vue tidak tahu persis apa yang Anda harapkan saat mengikatv-model atau menggunakan v-onsehingga semua itu harus diproses oleh pembuat komponen.

Cara menangani acara klik

Menurut Vue docs , $emitmeneruskan acara ke orang tua. Contoh dari dokumen:

File utama

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

Komponen

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

( @adalah v-on steno )

Komponen menangani clickacara asli dan memancarkan acara induk@enlarge-text="..."

enlarge-textdapat diganti dengan clickagar terlihat seperti kami menangani acara klik asli:

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

Tapi itu belum semuanya. $emitmemungkinkan untuk memberikan nilai tertentu dengan suatu peristiwa. Dalam kasus asli click, nilainya adalah MouseEvent (acara JS yang tidak ada hubungannya dengan Vue).

Vue menyimpan acara itu dalam suatu $eventvariabel. Jadi, yang terbaik adalah memancarkan $eventdengan sebuah acara untuk menciptakan kesan penggunaan acara asli:

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

Sedikit bertele-tele tetapi ini adalah bagaimana saya melakukannya:

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

Nep
sumber
8
kemana ini pergi? Mengapa Anda meletakkannya di sana? Tambahkan sedikit lebih detail untuk orang yang melihat jawaban ini, tolong?
mix3d
Dalam contoh ini ini akan ditempatkan pada tag div di komponen Test.vue. Anda kemudian dapat menggunakan v-on: click = "testFunction" atau @ click = "testFunction" saat menggunakan tes komponen di App.vue
Tim Wickstrom
Saya mengubahnya $emittetapi tidak ada yang terjadi. Apakah saya perlu melakukan sesuatu sebagai tambahan $emit? jsfiddle.net/xwvhy6a3
Richard Barraclough
@RichardBarraclough komponen Anda sekarang memancarkan acara khusus Anda "clickTreeItem". Berikutnya adalah menangani apa yang harus dilakukan dengan peristiwa itu dalam penggunaan komponen itu: v-on: myEvent = "myMethod"
Neps
5

Peristiwa asli komponen tidak dapat diakses langsung dari elemen induk. Sebagai gantinya Anda harus mencoba v-on:click.native="testFunction", atau Anda dapat memancarkan suatu peristiwa dari Testkomponen juga. Seperti v-on:click="$emit('click')".


sumber
5

Seperti yang disebutkan oleh Chris Fritz (Vue.js Core Team Emeriti ) di VueCONF US 2019

jika kita memiliki Kia masuk .nativedan kemudian elemen root dari input dasar berubah dari input ke label tiba-tiba komponen ini rusak dan itu tidak jelas dan pada kenyataannya, Anda bahkan mungkin tidak langsung menangkapnya kecuali Anda memiliki tes yang sangat bagus. Alih-alih dengan menghindari penggunaan .nativepengubah yang saat ini saya anggap sebagai anti-pola akan dihapus di Vue 3 Anda akan dapat secara eksplisit menentukan bahwa orang tua mungkin peduli tentang elemen pendengar yang ditambahkan ke ...

Dengan Vue 2

Menggunakan $listeners:

Jadi, jika Anda menggunakan Vue 2 pilihan yang lebih baik untuk menyelesaikan masalah ini adalah dengan menggunakan logika wrapper yang sepenuhnya transparan . Untuk Vue ini menyediakan $listenersproperti yang berisi objek pendengar yang digunakan pada komponen. Sebagai contoh:

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

dan kemudian kita hanya perlu menambahkan v-on="$listeners"ke testkomponen seperti:

Test.vue (komponen anak)

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

Sekarang <test>komponennya adalah pembungkus yang sepenuhnya transparan , artinya dapat digunakan persis seperti <div>elemen normal : semua pendengar akan bekerja, tanpa .nativepengubah.

Demo:

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>

Menggunakan $emitmetode:

Kita juga dapat menggunakan $emitmetode untuk tujuan ini, yang membantu kita mendengarkan peristiwa komponen anak dalam komponen induk. Untuk ini, pertama-tama kita harus memancarkan acara khusus dari komponen anak seperti:

Test.vue (komponen anak)

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

Penting: Selalu gunakan kotak kebab untuk nama acara. Untuk informasi lebih lanjut dan demo pembuatan ulang poin ini, silakan periksa jawaban ini: VueJS meneruskan nilai yang dihitung dari komponen ke induk .

Sekarang, kita hanya perlu mendengarkan acara kustom yang dipancarkan ini di komponen induk seperti:

Aplikasi

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

Jadi, pada dasarnya alih-alih v-on:clickatau singkatan @clickkami hanya akan menggunakan v-on:my-eventatau hanya @my-event.

Demo:

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>


Dengan Vue 3

Menggunakan v-bind="$attrs":

Vue 3 akan membuat hidup kita lebih mudah dalam banyak hal. Salah satu contoh untuk itu adalah itu akan membantu kita untuk membuat pembungkus transparan yang lebih sederhana dengan konfigurasi yang sangat sedikit hanya dengan menggunakan v-bind="$attrs". Dengan menggunakan ini pada komponen anak tidak hanya pendengar kita akan bekerja langsung dari orang tua tetapi juga atribut lain juga akan berfungsi seperti biasa<div> saja.

Jadi, sehubungan dengan pertanyaan ini, kami tidak perlu memperbarui apa pun di Vue 3 dan kode Anda masih akan berfungsi dengan baik seperti <div>elemen root di sini dan itu akan secara otomatis mendengarkan semua acara anak-anak.

Demo # 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>

Tetapi untuk komponen yang kompleks dengan elemen bersarang di mana kita perlu menerapkan atribut dan acara ke utama <input />alih-alih label induk kita cukup gunakanv-bind="$attrs"

Demo # 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>

palaѕн
sumber
1
Ini harus menjadi jawaban yang diterima. Terima kasih!
alijunior
0

Dari dokumentasi :

Karena keterbatasan dalam JavaScript, Vue tidak dapat mendeteksi perubahan berikut pada array:

  1. Ketika Anda langsung mengatur item dengan indeks, mis. Vm.items [indexOfItem] = newValue
  2. Saat Anda memodifikasi panjang array, mis. Vm.items.length = newLength

Dalam kasus saya, saya menemukan masalah ini ketika bermigrasi dari Angular ke VUE. Perbaikannya cukup mudah, tetapi sangat sulit ditemukan:

setValue(index) {
    Vue.set(this.arr, index, !this.arr[index]);
    this.$forceUpdate(); // Needed to force view rerendering
}
Andris
sumber