Vue v-on: щелчок не работает на компоненте
Я пытаюсь использовать директиву 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>
9 ответов
Если вы хотите прослушать собственное событие в корневом элементе компонента, вы должны использовать модификатор .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>
Я думаю $emit
Функция работает лучше, чем я думаю, что вы просите. Он хранит ваш компонент отдельно от экземпляра Vue, поэтому его можно многократно использовать во многих контекстах.
<template>
<div id="app">
<test @click="$emit('test-click')></test>
</div>
</template>
Используйте это в HTML
<test @test-click="testFunction">
Это ответ @Neps, но с подробностями.
Примечание: ответ @Saurabh будет более подходящим, если вы не хотите изменять свой компонент или не имеете к нему доступа.
Почему @click просто не работает?
Компоненты сложны. Один компонент может представлять собой небольшую необычную оболочку для кнопок, а другой - целую таблицу с кучей логики внутри. 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
чтобы это выглядело так, как будто мы обрабатываем собственное событие 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>
Как упоминал Крис Фриц (Vue.js Core Team Emeriti) в VueCONF US 2019
если бы у нас была Киа
.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>
element: все слушатели будут работать без .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>
Но для сложных компонентов с вложенными элементами, где нам нужно применить атрибуты и события к основным <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>
Немного многословно, но вот как я это делаю:
@click="$emit('click', $event)"
Нативные события компонентов не доступны напрямую из родительских элементов. Вместо этого вы должны попробовать v-on:click.native="testFunction"
или вы можете отправить событие из Test
компонент, а также. подобно v-on:click="$emit('click')"
,
Один из вариантов использования @click.native - это когда вы создаете настраиваемый компонент и хотите прослушивать событие щелчка на настраиваемом компоненте. Например:
#CustomComponent.vue
<div>
<span>This is a custom component</span>
</div>
#App.vue
<custom-component @click.native="onClick"></custom-component>
@click.native
всегда работайте в этой ситуации.
Из документации:
Из-за ограничений JavaScript Vue не может обнаружить следующие изменения в массиве:
- Когда вы напрямую устанавливаете элемент с индексом, например vm.items[indexOfItem] = newValue
- Когда вы изменяете длину массива, например vm.items.length = newLength
В моем случае я столкнулся с этой проблемой при переходе с Angular на VUE. Исправить было довольно просто, но найти действительно сложно:
setValue(index) {
Vue.set(this.arr, index, !this.arr[index]);
this.$forceUpdate(); // Needed to force view rerendering
}
App.vue
<div id="app">
<test @itemClicked="testFunction($event)"/>
</div>
Test.vue
<div @click="$emit('itemClicked', data)">
click here
</div>