Cara memanggil fungsi pada komponen anak pada acara induk

164

Konteks

Dalam Vue 2.0 dokumentasi dan lainnya dengan jelas menunjukkan bahwa komunikasi dari orang tua ke anak terjadi melalui alat peraga.

Pertanyaan

Bagaimana orang tua memberi tahu anaknya bahwa suatu peristiwa telah terjadi melalui alat peraga?

Haruskah saya menonton acara yang disebut prop? Itu tidak terasa benar, juga tidak ada alternatif ( $emit/ $onuntuk anak ke orang tua, dan model hub untuk elemen yang jauh).

Contoh

Saya memiliki wadah induk dan perlu memberi tahu wadah anaknya bahwa tidak apa-apa untuk melakukan tindakan tertentu pada API. Saya harus dapat memicu fungsi.

sangat
sumber

Jawaban:

234

Berikan komponen anak refdan gunakan $refsuntuk memanggil metode pada komponen anak secara langsung.

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);
    }
  }
})

Untuk info lebih lanjut, lihat dokumentasi Vue tentang referensi .

joerick
sumber
13
Dengan cara ini komponen induk dan anak menjadi berpasangan. Untuk acara nyata, katakanlah ketika Anda tidak bisa hanya mengubah prop untuk memicu aksi, saya akan pergi dengan solusi bus yang disarankan oleh @Roy J
Jared
3
ref ke dokumen akan sangat membantu juga vuejs.org/v2/guide/…
ctf0
Dalam komponen anak saya, untuk alasan khusus, saya harus menggunakan v-sekali untuk mengakhiri reaktivitas. Dengan demikian menurunkan prop dari orangtua ke anak bukanlah pilihan, jadi solusi ini berhasil!
John
1
pertanyaan pemula: Mengapa menggunakan refalih-alih membuat prop, yang menonton nilainya kemudian memancarkannya ke fungsi lain di induk? Maksud saya memang ada banyak hal yang harus dilakukan, tetapi apakah menggunakan refbahkan aman? Terima kasih
Irfandy Jip
5
@ IrfandyJip - ya, refaman. Secara umum, ini tidak disarankan karena komunitas Vue lebih suka memberikan status kepada anak-anak, dan acara kembali kepada orang tua. Secara umum, ini mengarah pada komponen yang lebih terisolasi, konsisten secara internal (hal yang baik ™). Tetapi, jika informasi yang Anda sampaikan kepada anak itu benar-benar suatu peristiwa (atau perintah), mengubah status bukanlah pola yang tepat. Dalam hal itu, memanggil metode menggunakan refbenar-benar baik, dan itu tidak akan crash atau apa pun.
joerick
76

Apa yang Anda gambarkan adalah perubahan status pada orang tua. Anda memberikannya kepada anak melalui alat peraga. Seperti yang Anda sarankan, Anda akan watchmendukung itu. Ketika anak mengambil tindakan, ia memberi tahu orang tua melalui emit, dan orang tua kemudian dapat mengubah negara lagi.

Jika Anda benar-benar ingin memberikan acara kepada seorang anak, Anda dapat melakukannya dengan membuat bus (yang hanya merupakan contoh Vue) dan memberikannya kepada anak sebagai penyangga .

Roy J
sumber
2
Saya pikir ini adalah satu-satunya jawaban yang sejalan dengan panduan gaya dan praktik terbaik Vue.JS resmi. Jika Anda menggunakan steno v-modelpada komponen, Anda juga dapat dengan mudah mengatur ulang nilainya dengan memancarkan peristiwa yang sesuai dengan kode yang lebih sedikit.
Falco
Misalnya, saya ingin memberi peringatan ketika pengguna mengklik tombol. Apakah Anda mengusulkan misalnya: - perhatikan bendera - atur bendera ini dari 0 hingga 1 saat klik terjadi, - lakukan sesuatu - atur ulang bendera
Sinan Erdem
9
Ini sangat tidak nyaman, Anda harus membuat ekstra proppada anak, properti tambahan di data, lalu menambahkan watch... Akan nyaman jika ada dukungan bawaan untuk entah bagaimana mentransfer acara dari orangtua ke anak. Situasi ini cukup sering terjadi.
Илья Зеленько
1
Sebagai status oleh @ ИльяЗеленько, itu memang cukup sering terjadi, itu akan menjadi anugerah saat ini.
Craig
1
Terima kasih @RoyJ, saya kira itu membutuhkan alat peraga bus untuk eksis ketika anak berlangganan, saya kira seluruh ide mengirimkan acara turun ke anak-anak tidak disarankan di Vue.
Ben Winding
35

Anda bisa menggunakan $emitdan $on. Menggunakan kode @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);
    }
  }
})

Contoh menjalankan: https://jsfiddle.net/rjurado/m2spy60r/1/

Drinor
sumber
6
Saya terkejut itu berhasil. Saya pikir memancarkan kepada seorang anak adalah anti-pola, atau bahwa maksudnya memancarkan hanya dari anak ke orang tua. Apakah ada potensi masalah dengan sebaliknya?
jbodily
2
Ini mungkin tidak dianggap sebagai cara terbaik, saya tidak tahu, tetapi jika Anda tahu apa yang Anda lakukan, saya tidak akan masalah. Cara lain adalah menggunakan bus pusat: vuejs.org/v2/guide/…
drinor
14
Ini menciptakan hubungan antara anak dan orang tua dan dianggap praktik yang buruk
morrislaptop
5
Ini hanya berfungsi karena orang tua bukan komponen tetapi sebenarnya aplikasi vue. Pada kenyataannya ini menggunakan contoh vue sebagai bus.
Julio Rodrigues
2
@Bsienn panggilan untuk ini. $ Parent membuat komponen ini bergantung pada parent. menggunakan $ emit to dan props sehingga satu-satunya dependensi adalah melalui sistem komunikasi Vue. Pendekatan ini memungkinkan komponen yang sama digunakan di mana saja dalam hierarki komponen.
morrislaptop
6

Jika Anda punya waktu, gunakan Vuex store untuk menonton variabel (alias status) atau memicu (alias mengirim) tindakan secara langsung.

brightknight08
sumber
2
karena reaktivitas vuejs / vuex yang merupakan pendekatan terbaik, orang tua membuat tindakan / mutasi yang mengubah nilai properti vuex dan pada anak memiliki nilai yang dihitung yang mendapatkan vuex $ store.state.property.value yang sama ini atau jam tangan " "metode yang melakukan sesuatu ketika vuex" $ store.state.property.value "berubah
FabianSilva
6

Tidak suka pendekatan event-bus menggunakan $onbinding pada anak selama create. Mengapa? createPanggilan selanjutnya (Saya menggunakanvue-router ) mengikat pawang pesan lebih dari sekali - mengarah ke beberapa respons per pesan.

Solusi ortodoks dari menurunkan alat peraga dari orangtua ke anak dan menempatkan pengamat properti pada anak itu sedikit berhasil lebih baik. Satu-satunya masalah adalah bahwa anak hanya dapat bertindak pada transisi nilai. Melewati pesan yang sama beberapa kali membutuhkan semacam pembukuan untuk memaksakan transisi agar anak dapat mengambil perubahan.

Saya telah menemukan bahwa jika saya membungkus pesan dalam sebuah array, itu akan selalu memicu pengamat anak - bahkan jika nilainya tetap sama.

Induk:

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

Anak:

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

HTML:

<parent>
   <child v-bind="{ 'msgChild': msgChild }"></child>
</parent>
Jason Stewart
sumber
1
Saya pikir ini tidak akan berfungsi jika msgChild selalu memiliki status yang sama pada induknya. Sebagai contoh: Saya ingin komponen yang membuka modal. Orang tua tidak peduli jika status saat ini terbuka atau ditutup, ia hanya ingin membuka modal kapan saja. Jadi, jika orang tua melakukan this.msgChild = true; modal ditutup, dan kemudian orang tua melakukan ini.msgChild = benar, anak tidak akan menerima acara
Jorge Sainz
1
@JorgeSainz: Itulah sebabnya saya membungkus nilai dalam array sebelum menetapkannya ke item data. Tanpa membungkus nilai dalam array, itu berperilaku seperti yang Anda tentukan. Jadi, msgChild = true, msgChild = true - tidak ada acara. msgChild = [true], msgChild = [true] - event!
Jason Stewart
1
Saya tidak melihatnya. Terima kasih atas klarifikasi
Jorge Sainz
Ini keren, tapi terasa agak seperti retas. Saya akan menggunakannya karena lebih bersih daripada menggunakan peretasan ref komponen dan tidak terlalu rumit dibandingkan dengan solusi event bus. Saya tahu bahwa vue ingin decoupling dan hanya memungkinkan perubahan keadaan untuk mempengaruhi komponen tetapi harus ada beberapa cara untuk memanggil metode anak jika diperlukan. Mungkin pengubah pada penyangga yang sekali berubah keadaan Anda bisa secara otomatis menyetel ulang ke nilai default sehingga pengamat siap untuk perubahan keadaan berikutnya. Pokoknya terima kasih telah memposting temuan Anda.
Craig
6

Cara sederhana dipisahkan untuk memanggil metode pada komponen anak adalah dengan memancarkan handler dari anak dan kemudian memohonnya dari orang tua.

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/[email protected]/dist/vue.js"></script>

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

Orang tua melacak fungsi penangan anak dan panggilan bila perlu.

nilobarp
sumber
Saya suka ke mana solusi ini pergi tetapi apa sebenarnya "this.setter" pada orang tua?
Craig
Ini adalah referensi fungsi setValue yang dipancarkan oleh komponen child sebagai argumen untuk menangani event.
nilobarp
2

Contoh di bawah ini cukup jelas. di mana referensi dan acara dapat digunakan untuk memanggil fungsi dari dan ke orangtua dan anak.

// 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>
Mukundhan
sumber
1

Saya pikir kita harus memiliki pertimbangan tentang perlunya orang tua untuk menggunakan metode anak. Bahkan, orang tua tidak perlu memperhatikan metode anak, tetapi dapat memperlakukan komponen anak sebagai FSA (mesin keadaan terbatas). Komponen orangtua untuk mengontrol keadaan komponen anak. Jadi solusi untuk menonton perubahan status atau hanya menggunakan fungsi komputasi sudah cukup

pengguna10097040
sumber
2
Jika Anda mengatakan "orang tua tidak boleh memedulikan diri mereka sendiri dengan mengendalikan anak-anak", ada kasus-kasus di mana hal ini diperlukan. Pertimbangkan komponen penghitung waktu mundur. Orangtua mungkin ingin mengatur ulang timer untuk memulai dari awal. Cukup menggunakan alat peraga tidak cukup karena pergi dari waktu = 60 ke waktu = 60 tidak akan mengubah prop. Timer harus mengekspos fungsi 'reset' yang dapat dipanggil oleh orang tua sebagaimana mestinya.
tbm
0

Anda dapat menggunakan kunci untuk memuat ulang komponen anak menggunakan kunci

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

Jika Anda ingin memuat ulang komponen dengan filter baru, jika klik tombol filter komponen anak

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

dan gunakan filter untuk memicu fungsi

Ardian Zack
sumber