Vue v-on: il clic non funziona sul componente


188

Sto cercando di utilizzare la direttiva on click all'interno di un componente ma non sembra funzionare. Quando faccio clic sul componente non succede nulla quando dovrei ottenere un "clic di prova" nella console. Non vedo alcun errore nella console, quindi non so cosa sto facendo di sbagliato.

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 (il componente)

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

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

Risposte:


335

Se vuoi ascoltare un evento nativo sull'elemento root di un componente, devi usare il modificatore .native per v-on, come segue:

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

o in stenografia, come suggerito nel commento, puoi anche fare:

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

4
O la scorciatoia@click.native="testFunction"
Pier

76
che cos'è un evento nativo o in cosa differisce dagli altri eventi normali ? Perché questo caso speciale per gli elementi radice ???
MrClan,


9
si sconsiglia l'uso del modificatore nativo in vue. Usalo solo se assolutamente necessario. Vedi @ jim-mcneely per il modo corretto di raggiungere questo obiettivo, ovvero emettere eventi dall'elemento figlio e quindi ripristinarlo nel genitore.
Deiknymi,

5
Ecco il link corretto agli eventi nativi
Alexander Kim,

32

Penso che la $emitfunzione funzioni meglio per quello che penso tu stia chiedendo. Mantiene il componente separato dall'istanza di Vue in modo che sia riutilizzabile in molti contesti.

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

Usalo in HTML

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

5
Credo che questa sia la risposta corretta. Gestire all'interno del componente il collegamento degli eventi. Non riguarda il componente padre di quale "versione" dell'evento click chiamare. In realtà lo implemento come risposta di seguito nel componente @click="$emit('click')"e in questo modo il componente genitore usa semplicemente il normale@click
Nelson Rodriguez,

2
Sono un po 'confuso. È possibile che la parte $ emit si trovi nel modello del componente test?
Stefan Fabian,

1
Cosa ha detto @NelsonRodriguez. Usa @click="$emit('click')".
Nifel,

20

È la risposta di @Neps ma con dettagli.


Nota : la risposta di @ Saurabh è più adatta se non si desidera modificare il componente o non si ha accesso ad esso.


Perché @click non funziona e basta?

I componenti sono complicati. Un componente può essere un piccolo wrapper di pulsanti di fantasia e un altro può essere un intero tavolo con dentro un mucchio di logica. Vue non sa esattamente cosa ti aspetti quando si associa v-modelo utilizza, v-onquindi tutto ciò dovrebbe essere elaborato dal creatore del componente.

Come gestire l'evento click

Secondo i documenti Vue , $emitpassa gli eventi al genitore. Esempio da documenti:

File principale

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

Componente

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

( @è la v-on scorciatoia )

Il componente gestisce l' clickevento nativo ed emette i genitori@enlarge-text="..."

enlarge-textpuò essere sostituito con clickper far sembrare che stiamo gestendo un evento clic nativo:

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

Ma non è tutto. $emitconsente di passare un valore specifico con un evento. Nel caso di nativo click, il valore è MouseEvent (evento JS che non ha nulla a che fare con Vue).

Vue memorizza quell'evento in una $eventvariabile. Quindi, sarebbe meglio emettere $eventcon un evento per creare l'impressione dell'uso di eventi nativi:

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

8

Un po 'prolisso ma è così che lo faccio:

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


8
dove va questo? Perché lo metti lì? Aggiungi qualche dettaglio in più per le persone che visualizzano questa risposta, per favore?
mix3d

In questo esempio questo verrebbe inserito nel tag div nel componente Test.vue. È quindi possibile utilizzare v-on: click = "testFunction" o @ click = "testFunction" quando si utilizza il test dei componenti in App.vue
Tim Wickstrom,

L'ho cambiato in $emitma non sta succedendo nulla. Devo fare qualcosa in aggiunta a $emit? jsfiddle.net/xwvhy6a3
Richard Barraclough il

@RichardBarraclough il componente ora emette il tuo evento personalizzato "clickTreeItem". Il prossimo è gestire cosa fare di quell'evento nell'uso di quel componente: v-on: myEvent = "myMethod"
Neps

6

Come menzionato da Chris Fritz (Vue.js Core Team Emeriti ) in VueCONF US 2019

se avessimo inserito Kia .nativee poi l'elemento radice dell'input di base fosse cambiato da un input a un'etichetta all'improvviso questo componente è rotto e non è ovvio e in effetti, potresti anche non prenderlo subito se non hai un test davvero buono. Invece, evitando l'uso del .nativemodificatore che attualmente considero un anti-pattern verrà rimosso in Vue 3 , sarai in grado di definire esplicitamente che al genitore potrebbe interessare quali elementi listener sono aggiunti a ...

Con Vue 2

Utilizzando $listeners:

Pertanto, se si utilizza Vue 2, un'opzione migliore per risolvere questo problema sarebbe quella di utilizzare una logica wrapper completamente trasparente . Per questo Vue fornisce una $listenersproprietà contenente un oggetto di listener utilizzato sul componente. Per esempio:

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

e quindi dobbiamo solo aggiungere v-on="$listeners"al testcomponente come:

Test.vue (componente figlio)

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

Ora il <test>componente è un wrapper completamente trasparente , il che significa che può essere utilizzato esattamente come un normale <div>elemento: tutti gli ascoltatori funzioneranno, senza il .nativemodificatore.

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>

Utilizzando il $emitmetodo:

Possiamo anche utilizzare il $emitmetodo per questo scopo, che ci aiuta ad ascoltare gli eventi dei componenti figlio nel componente padre. Per questo, dobbiamo prima emettere un evento personalizzato dal componente figlio come:

Test.vue (componente figlio)

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

Importante: utilizzare sempre il caso kebab per i nomi degli eventi. Per ulteriori informazioni e demo che registrano questo punto, controlla questa risposta: VueJS passa il valore calcolato dal componente al genitore .

Ora, dobbiamo solo ascoltare questo evento personalizzato emesso nel componente padre come:

App.vue

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

Quindi, fondamentalmente anziché v-on:clicko la stenografia @clickuseremo semplicemente v-on:my-evento semplicemente @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>


Con Vue 3

Utilizzando v-bind="$attrs":

Vue 3 renderà la nostra vita molto più semplice in molti modi. Uno degli esempi è che ci aiuterà a creare un wrapper trasparente più semplice con una configurazione molto minore semplicemente usando v-bind="$attrs". Usando questo sui componenti figlio non solo il nostro listener funzionerà direttamente dal genitore ma anche qualsiasi altro attributo funzionerà come se fosse un normale <div>.

Quindi, rispetto a questa domanda, non avremo bisogno di aggiornare nulla in Vue 3 e il tuo codice continuerà a funzionare bene come <div>l'elemento radice qui e ascolterà automaticamente tutti gli eventi figlio.

Demo n. 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>

Ma per componenti complessi con elementi nidificati in cui è necessario applicare attributi ed eventi a main <input />anziché all'etichetta principale, possiamo semplicemente usarev-bind="$attrs"

Demo n. 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
Questa dovrebbe essere la risposta accettata. Grazie!
alijunior

5

Gli eventi nativi dei componenti non sono direttamente accessibili dagli elementi padre. Invece dovresti provare v-on:click.native="testFunction", oppure puoi anche emettere un evento dal Testcomponente. Come v-on:click="$emit('click')".


0

Dalla documentazione :

A causa delle limitazioni in JavaScript, Vue non è in grado di rilevare le seguenti modifiche a un array:

  1. Quando si imposta direttamente un elemento con l'indice, ad esempio vm.items [indexOfItem] = newValue
  2. Quando si modifica la lunghezza dell'array, ad esempio vm.items.length = newLength

Nel mio caso mi sono imbattuto in questo problema durante la migrazione da Angular a VUE. La correzione è stata abbastanza semplice, ma davvero difficile da trovare:

setValue(index) {
    Vue.set(this.arr, index, !this.arr[index]);
    this.$forceUpdate(); // Needed to force view rerendering
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.