วิธีการเรียกฟังก์ชั่นเกี่ยวกับองค์ประกอบของเด็กในเหตุการณ์ผู้ปกครอง


164

บริบท

ใน Vue 2.0 เอกสารและอื่น ๆระบุอย่างชัดเจนว่าการสื่อสารจากพ่อแม่ถึงเด็กเกิดขึ้นผ่านอุปกรณ์ประกอบฉาก

คำถาม

ผู้ปกครองบอกให้เด็กรู้เหตุการณ์ที่เกิดขึ้นผ่านอุปกรณ์ประกอบฉากได้อย่างไร

ฉันควรจะดูเหตุการณ์ที่เรียกว่าเสา? ไม่รู้สึกว่าถูกต้องและไม่ทำทางเลือก ( $emit/ $onสำหรับเด็กสู่ผู้ปกครองและเป็นโมเดลฮับสำหรับองค์ประกอบที่อยู่ห่างไกล)

ตัวอย่าง

ฉันมีคอนเทนเนอร์หลักและจำเป็นต้องแจ้งคอนเทนเนอร์ย่อยของเด็กว่าการมีส่วนร่วมในการดำเนินการบางอย่างกับ API นั้นทำได้ดี ฉันต้องการที่จะสามารถเรียกใช้ฟังก์ชั่น

คำตอบ:


235

ให้องค์ประกอบลูกrefและใช้$refsในการเรียกวิธีการในองค์ประกอบเด็กโดยตรง

HTML:

<div id="app">
  <child-component ref="childComponent"></child-component>
  <button @click="click">Click</button>  
</div>

javascript:

var ChildComponent = {
  template: '<div>{{value}}</div>',
  data: function () {
    return {
      value: 0
    };
  },
  methods: {
    setValue: function(value) {
        this.value = value;
    }
  }
}

new Vue({
  el: '#app',
  components: {
    'child-component': ChildComponent
  },
  methods: {
    click: function() {
        this.$refs.childComponent.setValue(2.0);
    }
  }
})

สำหรับข้อมูลเพิ่มเติมโปรดดูที่เอกสาร Vue ใน refs


13
วิธีนี้จะกลายเป็นส่วนประกอบหลักและส่วนประกอบลูก สำหรับเหตุการณ์จริงพูดเมื่อคุณไม่สามารถเปลี่ยนเสาเพื่อกระตุ้นการกระทำฉันจะไปกับโซลูชันรถบัสที่แนะนำโดย @Roy J
Jared

3
อ้างอิงถึงเอกสารจะเป็นประโยชน์เช่นกันvuejs.org/v2/guide/ ......
ctf0

ในองค์ประกอบลูกของฉันด้วยเหตุผลพิเศษฉันต้องใช้ v- หนึ่งครั้งเพื่อยุติปฏิกิริยา ดังนั้นการส่งเสาลงมาจากพ่อแม่สู่ลูกจึงไม่ใช่ตัวเลือกดังนั้นวิธีนี้จึงเป็นเคล็ดลับ!
จอห์น

1
คำถาม newbie:ทำไมใช้refแทนการสร้างเสาที่ดูค่าแล้วปล่อยมันไปยังฟังก์ชั่นอื่นในผู้ปกครอง? ฉันหมายความว่ามันมีหลายอย่างที่ต้องทำ แต่ใช้refอย่างปลอดภัยเหรอ? ขอบคุณ
Irfandy Jip

5
@IrfandyJip - ใช่refปลอดภัยแล้ว โดยทั่วไปมันเป็นกำลังใจเพราะชุมชน Vue ชอบที่จะส่งต่อสถานะให้กับเด็ก ๆ และเหตุการณ์กลับไปที่ผู้ปกครอง โดยทั่วไปแล้วสิ่งนี้จะนำไปสู่องค์ประกอบที่แยกจากกันและมีความสอดคล้องกันภายใน (สิ่งที่ดี™) แต่ถ้าข้อมูลที่คุณส่งให้เด็กนั้นเป็นเหตุการณ์จริง (หรือคำสั่ง) การแก้ไขสถานะไม่ใช่รูปแบบที่ถูกต้อง ในกรณีนั้นการเรียกใช้เมธอดโดยใช้ a refนั้นใช้ได้ทั้งหมดและมันจะไม่ผิดพลาดหรืออะไรก็ตาม
joerick

77

สิ่งที่คุณกำลังอธิบายคือการเปลี่ยนแปลงของรัฐในผู้ปกครอง คุณส่งสิ่งนั้นให้กับเด็กผ่านเสา ตามที่คุณแนะนำคุณจะทำwatchเช่นนั้น เมื่อเด็กดำเนินการมันจะแจ้งให้ผู้ปกครองผ่านทางemitและผู้ปกครองอาจเปลี่ยนสถานะอีกครั้ง

ถ้าคุณต้องการอย่างแท้จริงที่จะผ่านเหตุการณ์ที่เกิดขึ้นกับบุตรหลานของคุณสามารถทำได้โดยการสร้างรถประจำทาง (ซึ่งเป็นเพียงอินสแตนซ์วู) และส่งผ่านไปยังเด็กที่เป็นเสา


2
ฉันคิดว่านี่เป็นคำตอบเดียวที่สอดคล้องกับแนวทางแบบ Vue.JS อย่างเป็นทางการและแนวทางปฏิบัติที่ดีที่สุด หากคุณใช้การจดชวเลขv-modelบนส่วนประกอบคุณสามารถรีเซ็ตค่าได้อย่างง่ายดายโดยการปล่อยเหตุการณ์ที่เกี่ยวข้องด้วยรหัสน้อย
Falco

ตัวอย่างเช่นฉันต้องการแจ้งเตือนเมื่อผู้ใช้คลิกปุ่ม คุณเสนอตัวอย่างเช่น: - ดูธง - ตั้งค่าสถานะนี้จาก 0 ถึง 1 เมื่อคลิกเกิดขึ้น - ทำอะไร - ตั้งค่าสถานะใหม่
Sinan Erdem

9
มันอึดอัดมากคุณต้องสร้างสิ่งพิเศษpropในเด็กคุณสมบัติพิเศษdataแล้วเพิ่มwatch... มันจะสบายถ้ามีการสนับสนุนในตัวเพื่อถ่ายโอนเหตุการณ์จากผู้ปกครองไปยังเด็ก สถานการณ์นี้เกิดขึ้นค่อนข้างบ่อย
ИльяЗеленько

1
ในฐานะรัฐโดย @ ИльяЗеленькоมันเกิดขึ้นบ่อยครั้งมันจะมาจากสวรรค์ในขณะนี้
Craig

1
ขอบคุณ @RoyJ ฉันเดาว่าต้องมีเสารถบัสอยู่เมื่อเด็กสมัครสมาชิกแม้ว่าฉันคิดว่าความคิดทั้งหมดในการส่งกิจกรรมไปยังเด็ก ๆ นั้นเป็นสิ่งที่ไม่ควรทำใน Vue
Ben Winding

35

คุณสามารถใช้และ$emit $onใช้รหัส @RoyJ:

HTML:

<div id="app">
  <my-component></my-component>
  <button @click="click">Click</button>  
</div>

javascript:

var Child = {
  template: '<div>{{value}}</div>',
  data: function () {
    return {
      value: 0
    };
  },
  methods: {
    setValue: function(value) {
        this.value = value;
    }
  },
  created: function() {
    this.$parent.$on('update', this.setValue);
  }
}

new Vue({
  el: '#app',
  components: {
    'my-component': Child
  },
  methods: {
    click: function() {
        this.$emit('update', 7);
    }
  }
})

ตัวอย่างการรัน: https://jsfiddle.net/rjurado/m2spy60r/1/


6
ฉันประหลาดใจที่มันใช้งานได้ ฉันคิดว่าการเปล่งออกมากับเด็กนั้นเป็นรูปแบบต่อต้านหรือความตั้งใจที่ปล่อยออกมาจากเด็กไปสู่ผู้ปกครองเท่านั้น มีปัญหาที่อาจเกิดขึ้นอีกหรือไม่?
jbodily

2
นี่อาจไม่ถือว่าเป็นวิธีที่ดีที่สุดฉันไม่รู้ แต่ถ้าคุณรู้ว่าคุณกำลังทำอะไรฉันไม่น่ามีปัญหา วิธีอื่นคือใช้รถบัสกลาง: vuejs.org/v2/guide/…
drinor

14
นี้จะสร้างการมีเพศสัมพันธ์ระหว่างเด็กและผู้ปกครองและถือปฏิบัติไม่ดี
morrislaptop

5
ใช้งานได้เพียงเพราะผู้ปกครองไม่ได้เป็นส่วนประกอบ แต่จริงๆแล้วเป็นแอป vue ในความเป็นจริงนี่คือการใช้อินสแตนซ์ vue เป็นรถบัส
Julio Rodrigues

2
@Bsienn เรียกสิ่งนี้ $ parent ทำให้ส่วนประกอบนี้ขึ้นอยู่กับพาเรนต์ ใช้ $ emit และ props เพื่อให้การอ้างอิงเท่านั้นผ่านระบบการสื่อสารของ Vue วิธีนี้ช่วยให้สามารถใช้ส่วนประกอบเดียวกันทุกที่ในลำดับชั้นของส่วนประกอบ
morrislaptop

6

หากคุณมีเวลาใช้ Vuex store เพื่อดูตัวแปร (สถานะ aka) หรือทริกเกอร์ (aka dispatch) การกระทำโดยตรง


2
เนื่องจาก reactivity ของ vuejs / vuex ที่ดีที่สุด aproach ในผู้ปกครองทำการกระทำ / การกลายพันธุ์ที่เปลี่ยนค่าคุณสมบัติ vuex และในเด็กมีค่าคำนวณที่ได้รับ vuex $ store.state.property.value เดียวกันนี้หรือ "ดู "เมธอดที่ทำบางสิ่งเมื่อ vuex" $ store.state.property.value "การเปลี่ยนแปลง
FabianSilva

6

ไม่ชอบวิธีการที่เหตุการณ์รถบัสใช้ผูกในเด็กในช่วง$on createทำไม? การcreateโทรครั้งต่อไป (ฉันใช้vue-router) ผูกตัวจัดการข้อความมากกว่าหนึ่งครั้ง - นำไปสู่การตอบกลับหลายครั้งต่อข้อความ

วิธีการแก้ปัญหาดั้งเดิมของการส่งผ่านอุปกรณ์ประกอบฉากลงมาจากพ่อแม่สู่ลูกและวางเฝ้าทรัพย์สินในเด็กที่ทำงานเล็ก ๆ น้อย ๆที่ดีกว่า ปัญหาเดียวคือการที่เด็กสามารถดำเนินการเกี่ยวกับการเปลี่ยนแปลงค่า การส่งข้อความเดียวกันหลายครั้งต้องมีการทำบัญชีบางอย่างเพื่อบังคับให้เปลี่ยนเพื่อให้เด็กสามารถรับการเปลี่ยนแปลงได้

ฉันพบว่าถ้าฉันใส่ข้อความลงในอาร์เรย์มันจะทำให้ผู้ดูแลเด็กดูอยู่เสมอแม้ว่าค่านั้นจะยังคงเหมือนเดิม

ต้นทาง:

{
   data: function() {
      msgChild: null,
   },
   methods: {
      mMessageDoIt: function() {
         this.msgChild = ['doIt'];
      }
   }   
   ...
}

เด็ก:

{
   props: ['msgChild'],
   watch: {
      'msgChild': function(arMsg) {
         console.log(arMsg[0]);
      }
   }
}

HTML:

<parent>
   <child v-bind="{ 'msgChild': msgChild }"></child>
</parent>

1
ฉันคิดว่าสิ่งนี้จะไม่ทำงานหาก msgChild มีสถานะเหมือนเดิมบนพาเรนต์เสมอ ตัวอย่างเช่น: ฉันต้องการส่วนประกอบที่เปิดเป็นกิริยาช่วย ผู้ปกครองไม่สนใจว่าสถานะปัจจุบันจะเปิดหรือปิดก็แค่ต้องการที่จะเปิดเป็นกิริยาช่วยได้ตลอดเวลา ดังนั้นหากพาเรนต์ทำ this.msgChild = true modal ถูกปิดแล้วผู้ปกครองทำสิ่งนี้. msgChild = จริงเด็กจะไม่ได้รับเหตุการณ์
Jorge Sainz

1
@ JorgeSainz: นั่นคือเหตุผลที่ฉันห่อค่าในอาร์เรย์ก่อนที่จะกำหนดให้กับรายการข้อมูล โดยไม่ต้องห่อค่าในอาร์เรย์มันจะทำงานตามที่คุณระบุ ดังนั้น msgChild = true, msgChild = true - ไม่มีเหตุการณ์ msgChild = [จริง], msgChild = [จริง] - เหตุการณ์!
Jason Stewart

1
ฉันไม่เห็นมัน ขอบคุณสำหรับความกระจ่าง
Jorge Sainz

มันเจ๋ง แต่ก็รู้สึกแฮ็คเล็กน้อย ฉันจะใช้มันเพราะมันสะอาดกว่าการใช้แฮ็กการอ้างอิงองค์ประกอบและซับซ้อนน้อยกว่าที่โซลูชันบัสเหตุการณ์ ฉันรู้ว่า vue ต้องการ decoupling และอนุญาตเฉพาะการเปลี่ยนแปลงสถานะเพื่อให้เกิดผลกระทบต่อส่วนประกอบ แต่ควรมีวิธีการในตัวที่จะเรียกวิธีการของเด็กถ้าจำเป็น บางทีตัวดัดแปลงบนเสาที่เมื่อเปลี่ยนสถานะคุณสามารถรีเซ็ตเป็นค่าเริ่มต้นโดยอัตโนมัติเพื่อให้ผู้เฝ้าดูพร้อมสำหรับการเปลี่ยนสถานะครั้งต่อไป อย่างไรก็ตามขอขอบคุณสำหรับการโพสต์การค้นหาของคุณ
Craig

6

วิธี decoupled อย่างง่าย ๆ ในการเรียกใช้เมธอดบนคอมโพเนนต์ชายน์คือการปล่อย handler จากชายด์จากนั้นเรียกใช้จาก parent

var Child = {
  template: '<div>{{value}}</div>',
  data: function () {
    return {
      value: 0
    };
  },
  methods: {
  	setValue(value) {
    	this.value = value;
    }
  },
  created() {
    this.$emit('handler', this.setValue);
  }
}

new Vue({
  el: '#app',
  components: {
    'my-component': Child
  },
  methods: {
  	setValueHandler(fn) {
    	this.setter = fn
    },
    click() {
    	this.setter(70)
    }
  }
})
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>

<div id="app">
  <my-component @handler="setValueHandler"></my-component>
  <button @click="click">Click</button>  
</div>

พาเรนต์ติดตามฟังก์ชันตัวจัดการเด็กและการโทรเมื่อจำเป็น


ฉันชอบที่การแก้ปัญหานี้จะไป แต่สิ่งที่เป็น "this.setter" ในผู้ปกครอง?
Craig

มันคือการอ้างอิงฟังก์ชั่น setValue ที่ปล่อยออกมาโดยองค์ประกอบเด็กเป็นข้อโต้แย้งกับเหตุการณ์จัดการ
nilobarp

2

ตัวอย่างด้านล่างคืออธิบายตนเอง ที่ refs และกิจกรรมที่สามารถใช้ในการเรียกใช้ฟังก์ชั่นจากและไปยังผู้ปกครองและเด็ก

// PARENT
<template>
  <parent>
    <child
      @onChange="childCallBack"
      ref="childRef"
      :data="moduleData"
    />
    <button @click="callChild">Call Method in child</button>
  </parent>
</template>

<script>
export default {
  methods: {
    callChild() {
      this.$refs.childRef.childMethod('Hi from parent');
    },
    childCallBack(message) {
      console.log('message from child', message);
    }
  }
};
</script>

// CHILD
<template>
  <child>
    <button @click="callParent">Call Parent</button>
  </child>
</template>

<script>
export default {
  methods: {
    callParent() {
      this.$emit('onChange', 'hi from child');
    },
    childMethod(message) {
      console.log('message from parent', message);
    }
  }
}
</script>

1

ฉันคิดว่าเราควรพิจารณาถึงความจำเป็นของผู้ปกครองในการใช้วิธีการของเด็กในความเป็นจริงผู้ปกครองไม่จำเป็นต้องกังวลเกี่ยวกับวิธีการของเด็ก แต่สามารถรักษาองค์ประกอบของเด็กเป็น FSA (เครื่องจักร จำกัด แน่นอน) เพื่อควบคุมสถานะขององค์ประกอบลูกดังนั้นวิธีการแก้ปัญหาเพื่อดูการเปลี่ยนแปลงสถานะหรือเพียงแค่ใช้ฟังก์ชั่นการคำนวณก็เพียงพอแล้ว


2
หากคุณกำลังพูดว่า "พ่อแม่ไม่ควรกังวลกับการควบคุมลูก" มีหลายกรณีที่จำเป็น พิจารณาองค์ประกอบตัวจับเวลานับถอยหลัง ผู้ปกครองอาจต้องการรีเซ็ตตัวจับเวลาเพื่อเริ่มต้นใหม่ การใช้อุปกรณ์ประกอบฉากนั้นไม่เพียงพอเนื่องจากการเปลี่ยนจากเวลา = 60 เป็นเวลา = 60 จะไม่เปลี่ยนเสา ตัวจับเวลาควรแสดงฟังก์ชั่น 'รีเซ็ต' ที่ผู้ปกครองสามารถเรียกได้ตามความเหมาะสม
tbm

0

คุณสามารถใช้รหัสเพื่อโหลดองค์ประกอบลูกโดยใช้รหัส

<component :is="child1" :filter="filter" :key="componentKey"></component>

ถ้าคุณต้องการที่จะโหลดองค์ประกอบที่มีตัวกรองใหม่ถ้าปุ่มคลิกกรององค์ประกอบที่เด็ก

reloadData() {            
   this.filter = ['filter1','filter2']
   this.componentKey += 1;  
},

และใช้ตัวกรองเพื่อเรียกใช้ฟังก์ชัน

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