Vue v-on: click không hoạt động trên thành phần


186

Tôi đang cố gắng sử dụng chỉ thị nhấp chuột bên trong một thành phần nhưng dường như nó không hoạt động. Khi tôi nhấp vào các thông báo thành phần sẽ xảy ra khi tôi nhận được 'nhấp vào thử nghiệm' trong bảng điều khiển. Tôi không thấy bất kỳ lỗi nào trong bảng điều khiển, vì vậy tôi không biết mình đang làm gì sai.

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>

Ứng dụng

<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 (thành phần)

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

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

Câu trả lời:


331

Nếu bạn muốn nghe một sự kiện riêng trên phần tử gốc của một thành phần, bạn phải sử dụng công cụ sửa đổi .native cho v-on, như sau:

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

hoặc viết tắt, như được đề xuất trong bình luận, bạn cũng có thể làm:

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

3
Hoặc tốc ký@click.native="testFunction"
Cầu tàu

75
một sự kiện bản địa là gì hoặc nó khác với các sự kiện bình thường khác như thế nào? Tại sao trường hợp đặc biệt này cho các yếu tố gốc ???
MrClan


9
sửa đổi bản địa được khuyên không nên được sử dụng trong vue. Chỉ sử dụng nếu thực sự cần thiết. Xem @ jim-mcneely để biết cách chính xác để đạt được điều này tức là phát ra các sự kiện từ phần tử con và sau đó khôi phục nó trong phần cha mẹ.
Deiknymi

5
Đây là liên kết chính xác đến các sự kiện bản địa
Alexander Kim

32

Tôi nghĩ rằng $emitchức năng hoạt động tốt hơn cho những gì tôi nghĩ rằng bạn đang yêu cầu. Nó giữ cho thành phần của bạn tách biệt với thể hiện Vue để nó có thể được sử dụng lại trong nhiều ngữ cảnh.

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

Sử dụng nó trong HTML

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

5
Tôi tin rằng đây là câu trả lời chính xác. Xử lý trong các thành phần liên kết của các sự kiện. Đừng quan tâm đến thành phần chính của "phiên bản" của sự kiện nhấp để gọi. Tôi thực sự thực hiện nó như là câu trả lời dưới đây trong thành phần @click="$emit('click')"và theo cách đó, thành phần cha mẹ chỉ cần sử dụng thông thường@click
Nelson Rodriguez

2
Tôi có chút bối rối. Phần $ emit có thể được cho là nằm trong mẫu thành phần thử nghiệm không?
Stefan Fabian

1
Những gì @NelsonRodriguez nói. Sử dụng @click="$emit('click')".
Nifel

20

Đó là câu trả lời của @Neps nhưng có chi tiết.


Lưu ý : Câu trả lời của @ Saurabh phù hợp hơn nếu bạn không muốn sửa đổi thành phần của mình hoặc không có quyền truy cập vào nó.


Tại sao @click không hoạt động?

Các thành phần phức tạp. Một thành phần có thể là một trình bao bọc nút ưa thích nhỏ và một thành phần khác có thể là toàn bộ bảng với một loạt logic bên trong. Vue không biết chính xác những gì bạn mong đợi khi liên kết v-modelhoặc sử dụng v-onvì vậy tất cả những điều đó nên được xử lý bởi người tạo thành phần.

Cách xử lý sự kiện click

Theo tài liệu của Vue , $emittruyền sự kiện cho phụ huynh. Ví dụ từ các tài liệu:

Tập tin chính

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

Thành phần

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

( @là tốc v-on )

Thành phần xử lý clicksự kiện riêng và phát ra cha mẹ@enlarge-text="..."

enlarge-textcó thể được thay thế bằng clickđể làm cho nó giống như chúng ta đang xử lý một sự kiện nhấp chuột gốc:

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

Nhưng đó không phải là tất cả. $emitcho phép vượt qua một giá trị cụ thể với một sự kiện. Trong trường hợp bản địa click, giá trị là MouseEvent (sự kiện JS không liên quan gì đến Vue).

Vue lưu trữ sự kiện đó trong một $eventbiến. Vì vậy, tốt nhất là phát ra $eventmột sự kiện để tạo ấn tượng về việc sử dụng sự kiện gốc:

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

8

Một chút dài dòng nhưng đây là cách tôi làm điều đó:

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


8
cái này đi đâu Tại sao bạn đặt nó ở đó? Xin vui lòng thêm một chút chi tiết cho những người xem câu trả lời này, xin vui lòng?
mix3d

Trong ví dụ này, điều này sẽ được đặt trên thẻ div trong thành phần Test.vue. Sau đó, bạn có thể sử dụng v-on: click = "testFunction" hoặc @ click = "testFunction" khi sử dụng thử nghiệm thành phần trong App.vue
Tim Wickstrom

Tôi đã thay đổi nó $emitnhưng không có gì xảy ra. Tôi có cần phải làm gì thêm $emitkhông? jsfiddle.net/xwvhy6a3
Richard Barraclough

@RichardBarraclough thành phần của bạn bây giờ phát ra sự kiện tùy chỉnh "clickTreeItem". Tiếp theo là xử lý những việc cần làm với sự kiện đó trong việc sử dụng thành phần đó: v-on: myEvent = "myMethod"
Neps

5

Các sự kiện riêng của các thành phần không thể truy cập trực tiếp từ các phần tử cha. Thay vào đó, bạn nên thử v-on:click.native="testFunction", hoặc bạn cũng có thể phát ra một sự kiện từ Testthành phần. Thích v-on:click="$emit('click')".


4

Như được đề cập bởi Chris Fritz (Vue.js Core Team Emeriti ) trong VueCONF US 2019

Nếu chúng ta có Kia nhập .nativevà sau đó phần tử gốc của đầu vào cơ sở đã thay đổi từ đầu vào thành nhãn đột nhiên thành phần này bị hỏng và không rõ ràng và trên thực tế, bạn thậm chí có thể không nắm bắt được ngay lập tức trừ khi bạn có bài kiểm tra thực sự tốt. Thay vào đó, bằng cách tránh sử dụng công cụ .nativesửa đổi mà tôi hiện đang xem là một kiểu chống mẫu sẽ bị xóa trong Vue 3, bạn có thể xác định rõ ràng rằng phụ huynh có thể quan tâm đến trình lắng nghe phần tử nào được thêm vào ...

Với Vue 2

Sử dụng $listeners:

Vì vậy, nếu bạn đang sử dụng Vue 2, một tùy chọn tốt hơn để giải quyết vấn đề này sẽ là sử dụng logic trình bao bọc hoàn toàn trong suốt . Đối với điều này, Vue cung cấp một thuộc $listenerstính có chứa một đối tượng người nghe đang được sử dụng trên thành phần. Ví dụ:

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

và sau đó chúng ta chỉ cần thêm v-on="$listeners"vào testthành phần như:

Test.vue (thành phần con)

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

Bây giờ <test>thành phần này là một trình bao bọc hoàn toàn trong suốt , có nghĩa là nó có thể được sử dụng chính xác như một <div>phần tử bình thường : tất cả các trình nghe sẽ hoạt động mà không cần .nativesửa đổi.

Bản giới thiệu:

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>

Sử dụng $emitphương pháp:

Chúng ta cũng có thể sử dụng $emitphương pháp cho mục đích này, giúp chúng ta lắng nghe các sự kiện thành phần con trong thành phần cha mẹ. Đối với điều này, trước tiên chúng ta cần phải phát ra một sự kiện tùy chỉnh từ thành phần con như:

Test.vue (thành phần con)

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

Quan trọng: Luôn sử dụng kebab-case cho tên sự kiện. Để biết thêm thông tin và bản demo lấy lại điểm này, vui lòng kiểm tra câu trả lời này: VueJS chuyển giá trị được tính từ thành phần sang phần gốc .

Bây giờ, chúng ta chỉ cần lắng nghe sự kiện tùy chỉnh được phát ra này trong thành phần cha mẹ như:

Ứng dụng

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

Vì vậy, về cơ bản thay vì v-on:clickhoặc tốc ký, @clickchúng ta sẽ chỉ sử dụng v-on:my-eventhoặc chỉ @my-event.

Bản giới thiệu:

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>


Với Vue 3

Sử dụng v-bind="$attrs":

Vue 3 sẽ làm cho cuộc sống của chúng ta dễ dàng hơn theo nhiều cách. Một trong những ví dụ cho nó là nó sẽ giúp chúng ta tạo ra một trình bao bọc trong suốt đơn giản hơn với cấu hình rất ít chỉ bằng cách sử dụng v-bind="$attrs". Bằng cách sử dụng điều này trên các thành phần con, không chỉ người nghe của chúng ta sẽ làm việc trực tiếp từ cha mẹ mà còn bất kỳ thuộc tính nào khác cũng sẽ hoạt động giống như nó một cách bình thường <div>.

Vì vậy, đối với câu hỏi này, chúng tôi sẽ không cần cập nhật bất cứ điều gì trong Vue 3 và mã của bạn sẽ vẫn hoạt động tốt như <div>là phần tử gốc ở đây và nó sẽ tự động lắng nghe tất cả các sự kiện con.

Bản thử nghiệm số 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>

Nhưng đối với các thành phần phức tạp với các thành phần lồng nhau, trong đó chúng ta cần áp dụng các thuộc tính và sự kiện cho chính <input />thay vì nhãn chính, chúng ta có thể chỉ cần sử dụngv-bind="$attrs"

Bản thử nghiệm số 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
Đây phải là câu trả lời được chấp nhận. Cảm ơn!
alijunior

0

Từ tài liệu :

Do những hạn chế trong JavaScript, Vue không thể phát hiện các thay đổi sau đối với một mảng:

  1. Khi bạn trực tiếp đặt một mục có chỉ mục, ví dụ: vm.items [indexOfItem] = newValue
  2. Khi bạn sửa đổi độ dài của mảng, vd.items.length = newLạng

Trong trường hợp của tôi, tôi đã vấp phải vấn đề này khi chuyển từ Angular sang VUE. Khắc phục khá dễ, nhưng thực sự khó tìm:

setValue(index) {
    Vue.set(this.arr, index, !this.arr[index]);
    this.$forceUpdate(); // Needed to force view rerendering
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.