Vue v-on: click funktioniert nicht für Komponenten

185

Ich versuche, die On-Click-Direktive in einer Komponente zu verwenden, aber sie scheint nicht zu funktionieren. Wenn ich auf die Komponente klicke, passiert nichts, wenn in der Konsole ein "Test angeklickt" werden soll. Ich sehe keine Fehler in der Konsole, daher weiß ich nicht, was ich falsch mache.

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 (die Komponente)

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

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

Antworten:

330

Wenn Sie ein natives Ereignis im Stammelement einer Komponente abhören möchten, müssen Sie den Modifikator .nativev-on wie folgt verwenden:

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

oder in Kurzform, wie im Kommentar vorgeschlagen, können Sie auch Folgendes tun:

<template>
  <div id="app">
    <test @click.native="testFunction"></test>
  </div>
</template>
Saurabh
quelle
3
Oder die Kurzschrift@click.native="testFunction"
Pier
75
Was ist ein natives Ereignis oder wie unterscheidet es sich von anderen normalen Ereignissen? Warum dieser Sonderfall für Wurzelelemente ???
MrClan
23
@ MrClan vuejs.org/v2/guide/…
user2875289
9
Es wird davon abgeraten, den nativen Modifikator in vue zu verwenden. Verwenden Sie es nur, wenn es unbedingt erforderlich ist. Unter @ jim-mcneely finden Sie den richtigen Weg, um dies zu erreichen, dh Ereignisse vom untergeordneten Element auszusenden und es dann im übergeordneten Element wiederzubeleben.
Deiknymi
5
Hier ist der richtige Link zu einheimischen Ereignissen
Alexander Kim
31

Ich denke, die $emitFunktion funktioniert besser für das, wonach Sie fragen. Dadurch wird Ihre Komponente von der Vue-Instanz getrennt, sodass sie in vielen Kontexten wiederverwendet werden kann.

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

Verwenden Sie es in HTML

// Parent component
<test @test-click="testFunction">
Jim McNeely
quelle
5
Ich glaube, das ist die richtige Antwort. Behandeln Sie innerhalb der Komponente die Verknüpfung der Ereignisse. Betrifft nicht die übergeordnete Komponente der "Version" des aufzurufenden Klickereignisses. Ich implementiere es tatsächlich als die Antwort unten in der Komponente @click="$emit('click')"und auf diese Weise verwendet die übergeordnete Komponente nur die reguläre@click
Nelson Rodriguez
2
Ich bin ein wenig verwirrt. Soll sich der $ emit-Teil möglicherweise in der Testkomponentenvorlage befinden?
Stefan Fabian
1
Was @NelsonRodriguez gesagt hat. Verwenden Sie @click="$emit('click')".
Nifel
20

Es ist die Antwort von @Neps, aber mit Details.


Hinweis : Die Antwort von @ Saurabh ist besser geeignet, wenn Sie Ihre Komponente nicht ändern möchten oder keinen Zugriff darauf haben.


Warum kann @click nicht einfach funktionieren?

Komponenten sind kompliziert. Eine Komponente kann ein kleiner ausgefallener Button-Wrapper sein, und eine andere kann eine ganze Tabelle mit einer Menge Logik sein. Vue weiß nicht genau, was Sie beim Binden v-modeloder Verwenden erwarten, v-ondaher sollte all dies vom Ersteller der Komponente verarbeitet werden.

Umgang mit Klickereignissen

Nach Vue docs , $emitgeht Ereignisse Eltern. Beispiel aus Dokumenten:

Hauptdatei

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

Komponente

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

( @ist die v-on Abkürzung )

Die Komponente behandelt native clickEreignisse und gibt übergeordnete Ereignisse aus@enlarge-text="..."

enlarge-textkann durch ersetzt werden click, damit es so aussieht, als würden wir ein natives Klickereignis behandeln:

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

Aber das ist nicht alles. $emitErmöglicht die Übergabe eines bestimmten Werts mit einem Ereignis. Im Fall von native clickist der Wert MouseEvent (JS-Ereignis, das nichts mit Vue zu tun hat).

Vue speichert dieses Ereignis in einer $eventVariablen. Es ist also am besten, $eventmit einem Ereignis zu emittieren , um den Eindruck einer nativen Ereignisnutzung zu erwecken:

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

Ein bisschen ausführlich, aber so mache ich es:

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

Neps
quelle
8
wohin führt das? Warum legst du es dort hin? Fügen Sie bitte ein wenig mehr Details für Personen hinzu, die diese Antwort sehen.
mix3d
In diesem Beispiel wird dies auf dem div-Tag in der Test.vue-Komponente platziert. Sie können dann v-on verwenden: click = "testFunction" oder @ click = "testFunction", wenn Sie den Komponententest in App.vue
Tim Wickstrom
Ich habe es geändert, $emitaber es passiert nichts. Muss ich zusätzlich noch etwas tun $emit? jsfiddle.net/xwvhy6a3
Richard Barraclough
@RichardBarraclough Ihre Komponente gibt jetzt Ihr benutzerdefiniertes Ereignis "clickTreeItem" aus. Als Nächstes wird behandelt, was mit diesem Ereignis bei der Verwendung dieser Komponente zu tun ist: v-on: myEvent = "myMethod"
Neps
5

Auf native Ereignisse von Komponenten kann von übergeordneten Elementen nicht direkt zugegriffen werden. Stattdessen sollten Sie es versuchen v-on:click.native="testFunction", oder Sie können auch ein Ereignis von einer TestKomponente ausgeben . Wie v-on:click="$emit('click')".


quelle
4

Wie von Chris Fritz ( Vue.js Kernteam Emeriti ) in VueCONF US 2019 erwähnt

Wenn wir Kia eingeben lassen .nativeund dann das Stammelement der Basiseingabe von einer Eingabe in eine Beschriftung geändert wird, ist diese Komponente plötzlich defekt und es ist nicht offensichtlich, und tatsächlich können Sie sie möglicherweise nicht sofort erfassen, es sei denn, Sie haben einen wirklich guten Test. Wenn Sie stattdessen vermeiden, dass der .nativeModifikator verwendet wird, von dem ich derzeit annehme, dass ein Anti-Pattern in Vue 3 entfernt wird, können Sie explizit definieren, dass es dem übergeordneten Element wichtig sein könnte, welche Element-Listener zu ...

Mit Vue 2

Verwenden von $listeners:

Wenn Sie also Vue 2 verwenden, ist die Verwendung einer vollständig transparenten Wrapper- Logik eine bessere Option, um dieses Problem zu beheben . Zu diesem Zweck stellt Vue eine $listenersEigenschaft bereit, die ein Objekt von Listenern enthält, die für die Komponente verwendet werden. Beispielsweise:

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

und dann müssen wir v-on="$listeners"der testKomponente nur noch Folgendes hinzufügen :

Test.vue (untergeordnete Komponente)

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

Jetzt ist die <test>Komponente ein vollständig transparenter Wrapper , was bedeutet, dass sie genau wie ein normales <div>Element verwendet werden kann: Alle Listener funktionieren ohne den .nativeModifikator.

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>

Mit $emitMethode:

Zu $emitdiesem Zweck können wir auch eine Methode verwenden, mit der wir Ereignisse von untergeordneten Komponenten in übergeordneten Komponenten abhören können. Dazu müssen wir zuerst ein benutzerdefiniertes Ereignis von einer untergeordneten Komponente wie folgt ausgeben :

Test.vue (untergeordnete Komponente)

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

Wichtig: Verwenden Sie für Ereignisnamen immer Kebab-Groß- / Kleinschreibung. Weitere Informationen und eine Demo zu diesem Punkt finden Sie in der folgenden Antwort: VueJS übergibt den berechneten Wert von der Komponente an die übergeordnete Komponente .

Jetzt müssen wir nur noch dieses ausgegebene benutzerdefinierte Ereignis in der übergeordneten Komponente abhören, wie:

App.vue

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

Also, im Grunde anstelle von v-on:clickoder der Abkürzung werden @clickwir einfach v-on:my-eventoder nur verwenden @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>


Mit Vue 3

Verwenden von v-bind="$attrs":

Vue 3 wird unser Leben in vielerlei Hinsicht viel einfacher machen. Eines der Beispiele dafür ist, dass es uns helfen wird, einen einfacheren transparenten Wrapper mit sehr wenig Konfiguration zu erstellen, indem wir nur verwenden v-bind="$attrs". Wenn Sie dies für untergeordnete Komponenten verwenden, funktioniert nicht nur unser Listener direkt vom übergeordneten Element aus, sondern auch jedes andere Attribut funktioniert genauso wie normal <div>.

In Bezug auf diese Frage müssen wir in Vue 3 nichts aktualisieren, und Ihr Code funktioniert weiterhin einwandfrei, ebenso <div>wie das Stammelement hier, und er hört automatisch alle untergeordneten Ereignisse ab.

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

Aber für komplexe Komponenten mit verschachtelten Elementen, bei denen wir Attribute und Ereignisse <input />anstelle der übergeordneten Bezeichnung auf main anwenden müssen, können wir sie einfach verwendenv-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>

palaSн
quelle
1
Dies sollte die akzeptierte Antwort sein. Vielen Dank!
Alijunior
0

Aus der Dokumentation :

Aufgrund von Einschränkungen in JavaScript kann Vue die folgenden Änderungen an einem Array nicht erkennen:

  1. Wenn Sie ein Element direkt mit dem Index festlegen, z. B. vm.items [indexOfItem] = newValue
  2. Wenn Sie die Länge des Arrays ändern, z. B. vm.items.length = newLength

In meinem Fall bin ich bei der Migration von Angular auf VUE auf dieses Problem gestoßen. Die Lösung war recht einfach, aber sehr schwer zu finden:

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