Vue v-on: натискання не працює на компоненті


186

Я намагаюся використовувати директиву 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>

Відповіді:


333

Якщо ви хочете слухати нативну подію на кореневому елементі компонента, вам слід скористатися модифікатором .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>

3
Або стенограма@click.native="testFunction"
Пір

75
що таке рідна подія чи чим вона відрізняється від інших нормальних подій? Чому цей особливий випадок для кореневих елементів ???
MrClan


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="$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>

8

Трохи багатослівний, але ось так я це роблю:

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


8
куди це йде? Чому ти його туди кладеш? Додайте трохи більше інформації для людей, які переглядають цю відповідь, будь ласка?
mix3d

У цьому прикладі це буде розміщено на тезі div у компоненті Test.vue. Потім можна використовувати v-on: click = "testFunction" або @ click = "testFunction" під час використання тесту компонентів у App.vue
Тім

Я змінив це, $emitале нічого не відбувається. Чи потрібно щось робити додатково $emit? jsfiddle.net/xwvhy6a3
Річард

@RichardBarraclough ваш компонент тепер випускає вашу власну подію "clickTreeItem". Далі слід вирішити, що робити з цією подією при використанні цього компонента: v-on: myEvent = "myMethod"
Непс

5

Рідні події компонентів не є доступними безпосередньо з батьківських елементів. Натомість слід спробувати v-on:click.native="testFunction", інакше ви можете передавати подію і з Testкомпонента. Як v-on:click="$emit('click')".


4

Як згадував Кріс Фріц (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>

Важливо: Для імен подій завжди використовуйте кебаб-регістр. Для отримання додаткової інформації та демонстрації демонстрації цього пункту, будь ласка, ознайомтеся з цією відповіддю: 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>


1
Це має бути прийнятою відповіддю. Дякую!
alijunior

0

З документації :

Через обмеження в JavaScript Vue не може виявити наступні зміни масиву:

  1. Коли ви безпосередньо встановлюєте елемент з індексом, наприклад, vm.items [indexOfItem] = newValue
  2. Коли ви змінюєте довжину масиву, наприклад, vm.items.length = newLength

У моєму випадку я натрапив на цю проблему під час переходу з кутового на VUE. Виправити було досить просто, але насправді важко знайти:

setValue(index) {
    Vue.set(this.arr, index, !this.arr[index]);
    this.$forceUpdate(); // Needed to force view rerendering
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.