VueJs 2.0 भव्य बच्चे से अपने भव्य माता-पिता घटक के लिए घटना का उत्सर्जन करता है


88

ऐसा लगता है कि Vue.js 2.0 एक भव्य बच्चे से उसके भव्य माता-पिता के घटक की घटनाओं का उत्सर्जन नहीं करता है।

Vue.component('parent', {
  template: '<div>I am the parent - {{ action }} <child @eventtriggered="performAction"></child></div>',
  data(){
    return {
      action: 'No action'
    }
  },
  methods: {
    performAction() { this.action = 'actionDone' }
  }
})

Vue.component('child', {
  template: '<div>I am the child <grand-child></grand-child></div>'
})

Vue.component('grand-child', {
  template: '<div>I am the grand-child <button @click="doEvent">Do Event</button></div>',
  methods: {
    doEvent() { this.$emit('eventtriggered') }
  }
})

new Vue({
  el: '#app'
})

यह JsField इस मुद्दे को हल करता है https://jsfiddle.net/y5dvkqbd/4/ , लेकिन दो घटनाओं को खाली करके:

  • एक भव्य बच्चे से लेकर मध्य घटक तक
  • फिर मध्य घटक से भव्य माता-पिता के लिए फिर से उत्सर्जित करना

इस मध्य घटना को जोड़ना दोहराव और अस्वाभाविक लगता है। क्या भव्य माता-पिता से सीधे बाहर निकलने का एक तरीका है, जिसके बारे में मुझे जानकारी नहीं है?

जवाबों:


65

Vue 2.4 ने पदानुक्रम का उपयोग करके घटनाओं को आसानी से पास करने का एक तरीका पेश किया vm.$listeners

से https://vuejs.org/v2/api/#vm-listeners :

इसमें पेरेंट-स्कोप v-onईवेंट श्रोताओं (बिना .nativeसंशोधक के) शामिल हैं। यह v-on="$listeners"पारदर्शी आवरण घटकों को बनाते समय उपयोगी - के माध्यम से एक आंतरिक घटक को नीचे पारित किया जा सकता है ।

का उपयोग कर नीचे दिए गए स्निपेट देखें v-on="$listeners"में grand-childमें घटक childटेम्पलेट:

Vue.component('parent', {
  template:
    '<div>' +
      '<p>I am the parent. The value is {{displayValue}}.</p>' +
      '<child @toggle-value="toggleValue"></child>' +
    '</div>',
  data() {
    return {
      value: false
    }
  },
  methods: {
    toggleValue() { this.value = !this.value }
  },
  computed: {
    displayValue() {
      return (this.value ? "ON" : "OFF")
    }
  }
})

Vue.component('child', {
  template:
    '<div class="child">' +
      '<p>I am the child. I\'m just a wrapper providing some UI.</p>' +
      '<grand-child v-on="$listeners"></grand-child>' +
    '</div>'
})

Vue.component('grand-child', {
  template:
    '<div class="child">' +
      '<p>I am the grand-child: ' +
        '<button @click="emitToggleEvent">Toggle the value</button>' +
      '</p>' +
    '</div>',
  methods: {
    emitToggleEvent() { this.$emit('toggle-value') }
  }
})

new Vue({
  el: '#app'
})
.child {
  padding: 10px;
  border: 1px solid #ddd;
  background: #f0f0f0
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  <parent></parent>
</div>


मैंने इसे vuex / बस / रूट को प्रस्तुत किए बिना सबसे साफ तरीका पाया। यह सभी इमिट फंक्शन को ऊपर की तरफ से गुजारेगा। आप वी-ऑन = "$ श्रोताओं" से पहले @ someEmit = "someEmitHandler" होने के बाद भी बच्चों के उत्सर्जन में टैप कर सकते हैं
Mathias Haugsbø

39

Vue समुदाय आमतौर पर इस तरह के मुद्दे को हल करने के लिए Vuex का उपयोग करने का पक्षधर है। कई स्थितियों में घटनाओं की आवश्यकता को समाप्त करते हुए, Vuex राज्य में परिवर्तन किया जाता है और डोम प्रतिनिधित्व बस उसी से बहता है।

यह कहते हुए कि, पुन: उत्सर्जित करना शायद अगला सबसे अच्छा विकल्प होगा, और अंतिम रूप से आप इस घटना के बारे में विस्तृत रूप से इस प्रश्न के उत्तर में दिए गए उत्तर के रूप में एक घटना बस का उपयोग करना चुन सकते हैं।

नीचे दिए गए उत्तर इस सवाल का मेरा मूल उत्तर है और ऐसा कोई दृष्टिकोण नहीं है जो मुझे अभी लेना होगा, जिसमें Vue के साथ अधिक अनुभव हो।


यह एक ऐसा मामला है जहाँ मैं Vue की डिज़ाइन पसंद और DOM का सहारा लेने से असहमत हो सकता हूँ।

में grand-child,

methods: {
    doEvent() { 
        try {
            this.$el.dispatchEvent(new Event("eventtriggered"));
        } catch (e) {
            // handle IE not supporting Event constructor
            var evt = document.createEvent("Event");
            evt.initEvent("eventtriggered", true, false);
            this.$el.dispatchEvent(evt);
        }
    }
}

और में parent,

mounted(){
    this.$el.addEventListener("eventtriggered", () => this.performAction())
}

अन्यथा, हां, आपको एक बस का पुन: उत्सर्जन या उपयोग करना होगा।

नोट: मैं IE को संभालने के लिए doEvent विधि में कोड जोड़ा गया; उस कोड को पुन: प्रयोज्य तरीके से निकाला जा सकता है।


यह IE के लिए अलग तरह से व्यवहार करता है? नहीं पता था कि vue के साथ ब्राउज़र की विसंगतियां थीं ...
BassMHL

@BassemLhm Vue IE के साथ ठीक है। IE के साथ समस्या Vue नहीं है, यह एक DOM समाधान है और आप IE में नया ईवेंट () नहीं कर सकते। आपको document.createEvent () डॉक्यूमेंट करना होगा। यदि आवश्यक हो तो मैं IE समर्थन जोड़ सकते हैं।
बर्ट

सिर्फ एक साधारण मामले के लिए vuex स्थापित करने का कोई मतलब नहीं है।
एडम

@AdamOrlov मैं आपसे सहमत हूँ।
बर्ट

एक सरल समाधान: stackoverflow.com/a/55650245/841591
digout

28

नई उत्तर (नवंबर 2018 अपडेट)

मुझे पता चला कि हम वास्तव $parentमें भव्य बाल घटक में संपत्ति का लाभ उठाकर ऐसा कर सकते हैं:

this.$parent.$emit("submit", {somekey: somevalue})

बहुत क्लीनर और सरल।


15
ध्यान दें कि यह केवल तभी काम करता है जब संबंध बच्चा हो -> दादा-दादी। यह काम नहीं करता है यदि बच्चे को मनमाने ढंग से गहरे स्तर पर घोंसला बनाया जा सकता है।
Qtax

मेरा उत्तर देखें stackoverflow.com/a/55650245/841591 @Qtax से टिप्पणी के जवाब में
Digout

7
आप अपने बड़े प्रोजेक्ट में इस तरह की चीजें नहीं चाहते हैं। आप 'बच्चे' को एक transitionया किसी अन्य आवरण घटक में डालते हैं और यह टूट जाएगा, जिससे आप अपने सिर पर एक बड़ा प्रश्न चिह्न लगा लेंगे।
एडम

@AdamOrlov मैं सहमत हूं, यह बुरा अभ्यास है। कृपया Vuex स्टोर का उपयोग करके इस तरह की घटनाओं को संभालें।
फेबियन वॉन एलर्ट्स

यह सरल है: stackoverflow.com/a/55650245/841591
8

26

हां, आप सही घटनाएं हैं जो केवल बच्चे से माता-पिता तक जाती हैं। वे आगे नहीं जाते हैं, जैसे बच्चे से दादा-दादी तक।

Vue प्रलेखन (संक्षेप में) गैर-अभिभावक-बाल संचार अनुभाग में इस स्थिति को संबोधित करता है ।

सामान्य विचार यह है कि दादा-दादी के घटक में आप एक खाली Vueघटक बनाते हैं जो दादा-दादी से नीचे के बच्चों और पोते के लिए प्रॉपर से गुजरता है। दादा-दादी तब घटनाओं और पोते के लिए सुनता है कि "घटना बस" पर घटनाओं का उत्सर्जन होता है।

कुछ एप्लिकेशन प्रति-घटक ईवेंट बस के बजाय वैश्विक ईवेंट बस का उपयोग करते हैं। वैश्विक ईवेंट बस का उपयोग करने का मतलब है कि आपको विशिष्ट ईवेंट नाम या नेमस्पेसिंग की आवश्यकता होगी ताकि ईवेंट अलग-अलग घटकों के बीच टकराए नहीं।

यहाँ एक उदाहरण है कि कैसे एक साधारण वैश्विक इवेंट बस को लागू किया जाए


16

रूट नोड पर एक अन्य समाधान / उत्सर्जन होगा :

भव्य-बच्चेvm.$root.$emit में उपयोग करता है , फिर पूर्वज (या कहीं भी आप चाहते हैं) पर उपयोग करता है ।vm.$root.$on

अपडेट किया गया : कभी कभी आप कुछ विशिष्ट स्थितियों में श्रोता निष्क्रिय करने के लिए, का उपयोग करें चाहते हैं वीएम बंद $। (उदाहरण के लिए: vm.$root.off('event-name')अंदर जीवन चक्र हुक = beforeDestroy )।

Vue.component('parent', {
  template: '<div><button @click="toggleEventListener()">Listener is {{eventEnable ? "On" : "Off"}}</button>I am the parent - {{ action }} <child @eventtriggered="performAction"></child></div>',
  data(){
    return {
      action: 1,
      eventEnable: false
    }
  },
  created: function () {
    this.addEventListener()
  },
  beforeDestroy: function () {
    this.removeEventListener()
  },
  methods: {
    performAction() { this.action += 1 },
    toggleEventListener: function () {
      if (this.eventEnable) {
        this.removeEventListener()
      } else {
        this.addEventListener()
      }
    },
    addEventListener: function () {
      this.$root.$on('eventtriggered1', () => {
        this.performAction()
      })
      this.eventEnable = true
    },
    removeEventListener: function () {
      this.$root.$off('eventtriggered1')
      this.eventEnable = false
    }
  }
})

Vue.component('child', {
  template: '<div>I am the child <grand-child @eventtriggered="doEvent"></grand-child></div>',
  methods: {
    doEvent() { 
    	//this.$emit('eventtriggered') 
    }
  }
})

Vue.component('grand-child', {
  template: '<div>I am the grand-child <button @click="doEvent">Emit Event</button></div>',
  methods: {
    doEvent() { this.$root.$emit('eventtriggered1') }
  }
})

new Vue({
  el: '#app'
})
<script src="https://unpkg.com/vue/dist/vue.js"></script>

<div id="app">
  <parent></parent>
</div>


मेरे लिए सबसे सुरुचिपूर्ण समाधान।
ओवलिया

अच्छा समाधान! यह मेरे लिए मुख्य रूप से सर्वर-साइड एप्लिकेशन (लारवेल) में कुछ Vue घटकों के साथ काम करता है (मैं एक Vuex परत को पेश नहीं करना चाहता था)। मेरा विशिष्ट मुद्दा एक कस्टम बटन घटक था जिसने एक फॉर्म घटक के साथ Buefy मोडल को ट्रिगर किया था। एक बार फॉर्म सबमिट हो जाने के बाद, मैं बटन को निष्क्रिय करना चाहता था, लेकिन फॉर्म से निकलने वाली कस्टम ईवेंट बटन तक नहीं पहुंचेंगे क्योंकि मोडल सीधे फॉर्म का मूल अभिभावक था।
रिचर्ड

यह श्रोता को नष्ट करने पर नष्ट नहीं करता है। क्या वह ठीक है? यदि आप उस तर्क को जोड़ सकते हैं तो बहुत अच्छा होगा, इसलिए मुझे पता है कि ऐसा कैसे करना है।
मेसकाइब

14

यदि आप लचीला होना चाहते हैं और बस एक घटना को सभी माता-पिता और उनके माता-पिता को पुन: मूल रूप से प्रसारित करते हैं, तो आप कुछ ऐसा कर सकते हैं:

let vm = this.$parent

while(vm) {
    vm.$emit('submit')
    vm = vm.$parent
}

1
यह सही जवाब है। मैंने अपने कोड में इसे कई स्थानों पर करने के लिए एक उपयोगिता फ़ंक्शन बनाया: propagateEvent (घटक, EventName, मान) {जबकि (घटक) {घटक। $ Emit (eventName, value); घटक = घटक। $ माता-पिता; }}
ccleve

2
यह एक सुंदर जवाब है, उपयोग करने के लिए बहुत सरल और पढ़ने में आसान है। और रैपिंग के अतिरिक्त स्तरों को जोड़कर इसे तोड़ा नहीं गया है।
डीन

4

यह एकमात्र मामला है जब मैं इवेंट बस का उपयोग करता हूँ !! गहरी नेस्टेड बच्चे से डेटा पास करने के लिए, सीधे माता-पिता के लिए नहीं, संचार।

पहला : इस सामग्री के साथ एक js फ़ाइल बनाएं (मैं इसे Eventbus.js नाम देता हूं:

import Vue from 'vue'    
Vue.prototype.$event = new Vue()

दूसरा : आपके बच्चे के घटक में एक घटना का उत्सर्जन होता है:

this.$event.$emit('event_name', 'data to pass')

तीसरा : माता-पिता में उस घटना को सुनें:

this.$event.$on('event_name', (data) => {
  console.log(data)
})

नोट: यदि आप उस घटना को अब और अनरजिस्टर्ड नहीं करना चाहते हैं:

this.$event.$off('event_name')

जानकारी: नीचे दी गई व्यक्तिगत राय को पढ़ने की आवश्यकता नहीं है

मैं ग्रैंड-पेरेंट कम्युनिकेशन (या इसी तरह के संचार स्तर) के लिए ग्रैंड-चाइल्ड के लिए vuex का उपयोग करना पसंद नहीं करता।

ग्रांड-माता-पिता से भव्य-बच्चे तक डेटा पास करने के लिए vue.js में आप प्रदान / इंजेक्शन का उपयोग कर सकते हैं । लेकिन इसके विपरीत चीज के लिए कुछ समान नहीं है। (ग्रैंड-चाइल्ड टू ग्रैंड-पैरेंट) इसलिए मैं इवेंट बस का उपयोग करता हूं जब भी मुझे उस तरह का संचार करना होता है।


2

मैंने @digout उत्तर के आधार पर एक छोटा मिश्रण बनाया है। आप इसे अपने Vue उदाहरण आरंभीकरण (नया Vue ...) से पहले इसे वैश्विक स्तर पर प्रोजेक्ट में उपयोग करने के लिए रखना चाहते हैं। आप इसे सामान्य घटना के समान उपयोग कर सकते हैं।

Vue.mixin({
  methods: {
    $propagatedEmit: function (event, payload) {
      let vm = this.$parent;
      while (vm) {
        vm.$emit(event, payload);
        vm = vm.$parent;
      }
    }
  }
})

यह समाधान वह है जो मैंने अपने कार्यान्वयन के लिए उपयोग किया था लेकिन मैंने एक अतिरिक्त पैरामीटर जोड़ा है targetRefजो आपके द्वारा लक्षित किए जा रहे घटक पर प्रसार को रोकता है। whileहालत शामिल तो हैं && vm.$refs[targetRef]- आप यह भी कहा कि शामिल करने के लिए आवश्यकता होगी refलक्षित घटक पर विशेषता , फायरिंग से कुछ घटनाओं और हो सकता है की कीमती नैनोसेकंड के एक जोड़े को बचाने के अपने प्रयोग के मामले में मैं सुरंग के लिए रूट करने के लिए सभी तरह की ज़रूरत नहीं थी समय
mcgraw

2

VueJS 2 घटकों में एक $parentसंपत्ति होती है जिसमें उनके मूल घटक होते हैं।

उस मूल घटक में स्वयं की $parentसंपत्ति भी शामिल होती है ।

फिर, "दादा-दादी" घटक को एक्सेस करना "माता-पिता के माता-पिता" घटक तक पहुंचने की बात है:

this.$parent["$parent"].$emit("myevent", { data: 123 });

वैसे भी, यह थोड़े मुश्किल है , और मैं एक वैश्विक राज्य प्रबंधक जैसे कि वेक्स या इसी तरह के टूल का उपयोग करने की सलाह देता हूं, जैसा कि अन्य उत्तरदाताओं ने कहा है।


1

@Kubaklam और @ डिगआउट के जवाबों से पीछे हटते हुए, यह वही है जो मैं ग्रैंड-चाइल्ड और (संभवतः दूर) दादा-दादी के बीच हर माता-पिता घटक पर उत्सर्जन से बचने के लिए उपयोग करता हूं:

{
  methods: {
    tunnelEmit (event, ...payload) {
      let vm = this
      while (vm && !vm.$listeners[event]) {
        vm = vm.$parent
      }
      if (!vm) return console.error(`no target listener for event "${event}"`)
      vm.$emit(event, ...payload)
    }
  }
}

जब आप दूर के भव्य बच्चों के साथ एक घटक का निर्माण करते हैं, जहाँ आप नहीं चाहते कि कई / कोई घटक स्टोर से बंधे हों, फिर भी रूट घटक को स्टोर / सत्य के स्रोत के रूप में कार्य करना चाहते हैं, यह काफी अच्छी तरह से काम करता है। यह एम्बर के दर्शन के डेटा डाउन एक्शन के समान है। नकारात्मक पक्ष यह है कि यदि आप बीच में हर माता-पिता पर उस घटना को सुनना चाहते हैं, तो यह काम नहीं करेगा। लेकिन फिर आप @kubaklam द्वारा उपरोक्त उत्तर में $ propogateEmit का उपयोग कर सकते हैं।

संपादित करें: प्रारंभिक vm को घटक पर सेट किया जाना चाहिए, न कि घटक के माता-पिता के लिए। Ie let vm = thisऔर नहींlet vm = this.$parent


0

मैं वास्तव में उस तरह से खुदाई करता हूं जो एक वर्ग बनाकर होता है जो खिड़की से जुड़ा होता है और जहां भी आप Vue ऐप में होते हैं, वहां काम करने के लिए प्रसारण / सुनो सेटअप को सरल बनाते हैं।

window.Event = new class {

    constructor() {
        this.vue = new Vue();
    }

    fire(event, data = null) {
        this.vue.$emit(event, data);
    }

    listen() {
        this.vue.$on(event, callback);  
    }

}

अब आप कॉल करके कहीं से भी आग लगा सकते हैं / प्रसारण कर सकते हैं / जो भी हो:

Event.fire('do-the-thing');

... और आप माता-पिता, दादा-दादी, जो कुछ भी आप फोन करके चाहते हैं, में सुन सकते हैं:

Event.listen('do-the-thing', () => {
    alert('Doing the thing!');
});

1
मैं विंडो ऑब्जेक्ट के लिए यादृच्छिक गुणों को संलग्न करने की अत्यधिक सलाह दूंगा, क्योंकि मौजूदा 3 पार्टी पुस्तकालयों के साथ मौजूदा गुणों या संघर्ष को अधिलेखित करना बहुत आसान है। इसके बजाय, इस समस्या को हल करने के लिए वीयू का उपयोग करने वाले किसी व्यक्ति को इसके बजाय @roli roli के उत्तर का उपयोग करना चाहिए
HMilbradt

1
मुझे यकीन नहीं है कि मैं इस चिंता से पूरी तरह से सहमत या सहमत हूं। प्रोटोटाइप से बांधना एक अच्छा तरीका है लेकिन खिड़की से बांधना वैसा ही है, अगर इससे भी अधिक सामान्य और शायद इसे संभालने का अधिक मानक तरीका नहीं है। आप संपत्ति को नाम देते हैं, इसलिए नामकरण संघर्ष से बचने के लिए यह सरल है। medium.com/@amitavroy7/… stackoverflow.com/questions/15008464/… यह भी लार्कास्ट पर जेफ वे का प्रस्तावित समाधान है। laracasts.com/series/learn-vue-2-step-by-step/episodes/13
fylzero
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.