Vue v-on: щелчок не работает на компоненте

188

Я пытаюсь использовать директиву 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>
Хавьер Карденас
источник

Ответы:

335

Если вы хотите прослушать собственное событие в корневом элементе компонента, вы должны использовать модификатор .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>
Саурабх
источник
4
Или стенография@click.native="testFunction"
Пирс
76
Что такое нативное событие или чем оно отличается от других обычных событий? Почему этот особый случай для корневых элементов ???
MrClan
24
@MrClan vuejs.org/v2/guide/...
user2875289
9
Родной модификатор не рекомендуется использовать в vue. Используйте его только в случае крайней необходимости. Смотрите @ jim-mcneely, чтобы узнать, как правильно этого добиться, то есть отправлять события из дочернего элемента и затем восстанавливать его в родительском элементе.
Deiknymi
5
Вот правильная ссылка на родные события
Александр Ким
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 вызывать. Я на самом деле реализую это как ответ ниже в компоненте @click="$emit('click')"и таким образом родительский компонент просто использует обычный@click
Нельсон Родригес,
2
Я немного запутался. Может быть, часть $ emit должна быть в шаблоне тестового компонента?
Стефан Фабиан
1
Что сказал @NelsonRodriguez. Использование @click="$emit('click')".
Нифель
20

Это ответ @Neps, но с подробностями.


Примечание : ответ @ Saurabh будет более подходящим, если вы не хотите изменять свой компонент или не имеете к нему доступа.


Почему @click просто не работает?

Компоненты сложны. Один компонент может представлять собой небольшую необычную оболочку для кнопок, а другой - целую таблицу с кучей логики внутри. Vue не знает, что именно вы ожидаете при связывании v-modelили использовании, v-onпоэтому все это должно обработать создатель компонента.

Как обрабатывать событие клика

Согласно документам Vue , $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позволяет передать определенное значение с событием. В случае с роднымclick значение MouseEvent (событие JS, которое не имеет ничего общего с Vue).

Vue сохраняет это событие в $eventпеременной. Итак, было бы лучше $eventсоздать событие, чтобы создать впечатление от использования нативного события:

<button v-on:click="$emit('click', $event)">
  Enlarge text
</button>
OddMorning
источник
8

Немного многословно, но вот как я это делаю:

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

NEPS
источник
8
куда это идет? Почему вы положили это там? Добавьте немного больше информации для людей, просматривающих этот ответ, пожалуйста?
mix3d
В этом примере это будет помещено в тег div в компоненте Test.vue. Затем вы можете использовать v-on: click = "testFunction" или @ click = "testFunction", когда используете компонентный тест в App.vue
Тим
Я изменил это, $emitно ничего не происходит. Нужно ли что-то делать в дополнение к $emit? jsfiddle.net/xwvhy6a3
Ричард Барракло
@RichardBarraclough, теперь ваш компонент генерирует ваше пользовательское событие "clickTreeItem". Далее следует обращаться , что делать с этим событием в использовании этого компонента: V-на: MyEvent = «MyMethod»
NEPS
7

Как отметил Крис Фриц (Vue.js Core Team Emeriti ) в VueCONF US 2019

если бы у нас был ввод Kia, .nativeа затем корневой элемент базового ввода изменился с входа на метку, внезапно этот компонент сломался, и это не очевидно, и на самом деле, вы могли бы даже не поймать его сразу, если у вас не будет действительно хорошего теста. Вместо этого, избегая использования .nativeмодификатора, который в настоящее время я считаю, анти-шаблон будет удален в Vue 3, вы сможете явно определить, что родитель может заботиться о том, какие слушатели элемента добавляются в ...

С 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>

Важно: Всегда используйте kebab-case для имен событий. Для получения дополнительной информации и демонстрации этого пункта, пожалуйста, ознакомьтесь с ответом: 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 собирается сделать нашу жизнь намного проще во многих отношениях. Одним из примеров этого является то, что это поможет нам создать более простую прозрачную оболочку с очень небольшим количеством настроек, просто используя 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>

Но для сложных компонентов с вложенными элементами, где нам нужно применять атрибуты и события к main <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>

palaѕн
источник
1
Это должен быть принятый ответ. Спасибо!
Алиюниор
5

Нативные события компонентов не доступны напрямую из родительских элементов. Вместо этого вы должны попробовать v-on:click.native="testFunction", или вы также можете отправить событие из Testкомпонента. Как v-on:click="$emit('click')".


источник
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
}
Андрис
источник