Vue v-on: การคลิกไม่ทำงานกับส่วนประกอบ


186

ฉันพยายามใช้คำสั่งคลิกเมื่อภายในองค์ประกอบ แต่ดูเหมือนจะไม่ทำงาน เมื่อฉันคลิกส่วนประกอบที่ไม่มีอะไรเกิดขึ้นเมื่อฉันควรได้รับ 'คลิกทดสอบ' ในคอนโซล ฉันไม่เห็นข้อผิดพลาดใด ๆ ในคอนโซลดังนั้นฉันไม่รู้ว่าฉันทำอะไรผิด

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
native modifier ไม่แนะนำให้ใช้ใน vue ใช้เมื่อจำเป็นเท่านั้น ดู @ jim-mcneely สำหรับวิธีการที่ถูกต้องเพื่อให้บรรลุเช่นการปล่อยเหตุการณ์จากองค์ประกอบย่อยแล้วฟื้นฟูในแม่
Deiknymi

5
นี่คือลิงก์ที่ถูกต้องไปยังกิจกรรมพื้นเมือง
Alexander Kim

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 อาจจะอยู่ในเทมเพลตองค์ประกอบการทดสอบหรือไม่
Stefan Fabian

1
สิ่งที่ @NelsonRodriguez พูด @click="$emit('click')"ใช้
Nifel

20

นี่คือคำตอบของ @Nepsแต่มีรายละเอียด


หมายเหตุ : คำตอบของ @ Saurabhเหมาะสมกว่าหากคุณไม่ต้องการแก้ไขส่วนประกอบของคุณหรือไม่สามารถเข้าถึงได้


เหตุใด @click จึงไม่ทำงาน

ส่วนประกอบมีความซับซ้อน องค์ประกอบหนึ่งสามารถเป็นเสื้อคลุมปุ่มแฟนซีเล็ก ๆ และอีกหนึ่งสามารถเป็นตารางทั้งหมดที่มีตรรกะภายใน Vue ไม่ทราบว่าคุณคาดหวังอะไรเมื่อผูกv-modelหรือใช้v-onดังนั้นสิ่งเหล่านี้ควรได้รับการประมวลผลโดยผู้สร้างส่วนประกอบ

วิธีจัดการเหตุการณ์คลิก

ตามเอกสารวู ,$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

verbose เล็กน้อย แต่นี่เป็นวิธีที่ฉันทำ:

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


8
มันจะไปไหน ทำไมคุณถึงใส่ไว้ที่นั่น? เพิ่มรายละเอียดเพิ่มเติมเล็กน้อยสำหรับผู้ที่ดูคำตอบนี้ใช่ไหม
mix3d

ในตัวอย่างนี้สิ่งนี้จะถูกวางไว้บนแท็ก div ในองค์ประกอบ Test.vue จากนั้นคุณสามารถใช้ v-on: click = "testFunction" หรือ @ click = "testFunction" เมื่อใช้การทดสอบส่วนประกอบใน App.vue
Tim Wickstrom

ฉันเปลี่ยนเป็น$emitแต่ไม่มีอะไรเกิดขึ้น ฉันต้องทำอะไรเพิ่มเติม$emitหรือไม่? jsfiddle.net/xwvhy6a3
Richard Barraclough

@RichardBarraclough ส่วนประกอบของคุณในขณะนี้ปล่อยเหตุการณ์ที่กำหนดเองของคุณ "clickTreeItem" ถัดไปคือจัดการสิ่งที่เกี่ยวข้องกับเหตุการณ์นั้นในการใช้งานองค์ประกอบนั้น: v-on: myEvent = "myMethod"
Neps

5

เหตุการณ์ดั้งเดิมของส่วนประกอบไม่สามารถเข้าถึงได้โดยตรงจากองค์ประกอบหลัก คุณควรลองแทนv-on:click.native="testFunction"หรือคุณสามารถเปล่งเหตุการณ์จากTestองค์ประกอบได้เช่นกัน กดv-on:click="$emit('click')"ไลค์


4

ดังกล่าวโดย Chris Fritz (Vue.js Core Team Emeriti ) ในVueCONF US 2019

ถ้าเรามี Kia เข้า.nativeแล้วองค์ประกอบรากของอินพุตพื้นฐานเปลี่ยนจากอินพุตเป็นฉลากโดยฉับพลันส่วนประกอบนี้ขาดและไม่ชัดเจนและในความเป็นจริงคุณอาจไม่ได้จับทันทีถ้าคุณมีการทดสอบที่ดีจริงๆ แต่โดยการหลีกเลี่ยงการใช้.nativeตัวปรับแต่งซึ่งปัจจุบันฉันคิดว่ารูปแบบการต่อต้านจะถูกลบใน Vue 3คุณจะสามารถกำหนดได้อย่างชัดเจนว่าผู้ปกครองอาจสนใจว่าองค์ประกอบใดที่ผู้ฟังจะถูกเพิ่มลงใน ...

ด้วย Vue 2

การใช้ $listeners :

ดังนั้นหากคุณใช้ Vue 2 ตัวเลือกที่ดีกว่าในการแก้ไขปัญหานี้คือการใช้ตรรกะของwrapper ที่โปร่งใส สำหรับ Vue นี้จัดเตรียม$listenersคุณสมบัติที่มีอ็อบเจ็กต์ของ listeners ที่ถูกใช้บนคอมโพเนนต์ ตัวอย่างเช่น:

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

แล้วเราก็ต้องเพิ่มv-on="$listeners"เข้าไปtestองค์ประกอบที่ชอบ:

Test.vue (องค์ประกอบลูก)

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

ตอนนี้<test>คอมโพเนนต์เป็นwrapper โปร่งใสอย่างสมบูรณ์ซึ่งหมายความว่าสามารถใช้งานได้เหมือนกับ<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 กำลังจะทำให้ชีวิตของเราง่ายขึ้นในหลาย ๆ ด้าน หนึ่งในตัวอย่างของมันคือมันจะช่วยให้เราสร้างwrapper โปร่งใสที่ง่ายขึ้นโดยใช้ config น้อยกว่าเพียงแค่ใช้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>


1
นี่ควรเป็นคำตอบที่ยอมรับได้ ขอบคุณ!
alijunior

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
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.