Panggil metode komponen Vue.js dari luar komponen

229

Katakanlah saya memiliki instance Vue utama yang memiliki komponen anak. Apakah ada cara memanggil metode milik salah satu komponen ini dari luar instance Vue sepenuhnya?

Berikut ini sebuah contoh:

var vm = new Vue({
  el: '#app',
  components: {
    'my-component': { 
      template: '#my-template',
      data: function() {
        return {
          count: 1,
        };
      },
      methods: {
        increaseCount: function() {
          this.count++;
        }
      }
    },
  }
});

$('#external-button').click(function()
{
  vm['my-component'].increaseCount(); // This doesn't work
});
<script src="http://vuejs.org/js/vue.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="app">
  
  <my-component></my-component>
  <br>
  <button id="external-button">External Button</button>
</div>
  
<template id="my-template">
  <div style="border: 1px solid; padding: 5px;">
  <p>A counter: {{ count }}</p>
  <button @click="increaseCount">Internal Button</button>
    </div>
</template>

Jadi ketika saya mengklik tombol internal, increaseCount()metode ini terikat ke event klik sehingga dipanggil. Tidak ada cara untuk mengikat acara ke tombol eksternal, yang acara kliknya saya dengarkan dengan jQuery, jadi saya perlu cara lain untuk menelepon increaseCount.

EDIT

Tampaknya ini berfungsi:

vm.$children[0].increaseCount();

Namun, ini bukan solusi yang baik karena saya mereferensikan komponen dengan indeksnya di array anak-anak, dan dengan banyak komponen ini tidak mungkin tetap konstan dan kode kurang dapat dibaca.

harryg
sumber
1
Saya menambahkan jawaban menggunakan mxins jika Anda ingin mencobanya. Menurut pendapat saya, saya lebih suka mengatur aplikasi dengan cara ini.
Omar Tanti

Jawaban:

275

Pada akhirnya saya memilih untuk menggunakan arahan Vueref . Ini memungkinkan komponen untuk dirujuk dari induk untuk akses langsung.

Misalnya

Minta kompenen terdaftar di perusahaan induk saya:

var vm = new Vue({
    el: '#app',
    components: { 'my-component': myComponent }
});

Render komponen dalam template / html dengan referensi:

<my-component ref="foo"></my-component>

Sekarang, di tempat lain saya dapat mengakses komponen secara eksternal

<script>
vm.$refs.foo.doSomething(); //assuming my component has a doSomething() method
</script>

Lihat biola ini untuk contoh: https://jsfiddle.net/xmqgnbu3/1/

(contoh lama menggunakan Vue 1: https://jsfiddle.net/6v7y6msr/ )

harryg
sumber
6
Itu karena Anda mungkin belum mendefinisikannya. Lihatlah biola yang ditautkan.
harryg
29
Jika Anda menggunakan webpack maka Anda tidak akan dapat mengakses vmkarena cara itu meringkas modul. Anda dapat melakukan sesuatu seperti window.app = vmdi blog Anda main.js. Sumber: forum.vuejs.org/t/how-to-access-vue-from-chrome-console/3606
tw airball
1
Bisakah pendekatan ini dianggap normal? Atau ini hack?
CENT1PEDE
3
Ada definisi resmi tentang apa itu peretasan vs apa yang merupakan kode "normal", tetapi daripada menyebut pendekatan ini peretasan (atau mencari cara "kurang peretasan" untuk mencapai hal yang sama) mungkin lebih baik untuk mempertanyakan mengapa Anda perlu melakukan ini. Dalam banyak kasus mungkin lebih elegan menggunakan sistem acara Vue untuk memicu perilaku komponen eksternal, atau bahkan bertanya mengapa Anda memicu komponen secara eksternal sama sekali.
harryg
8
Ini dapat muncul jika Anda mencoba mengintegrasikan komponen tampilan ke halaman yang sudah ada. Daripada mendesain ulang seluruh halaman, lebih baik terkadang menambahkan fungsi tambahan secara bertahap.
Allen Wang
37

Sejak Vue2 ini berlaku:

var bus = new Vue()

// dalam metode komponen A

bus.$emit('id-selected', 1)

// di kait komponen B yang dibuat

bus.$on('id-selected', function (id) {

  // ...
})

Lihat di sini untuk dokumentasi Vue. Dan di sini lebih detail tentang bagaimana mengatur bus acara ini.

Jika Anda ingin info lebih lanjut tentang kapan menggunakan properti, acara, dan / atau manajemen negara terpusat lihat artikel ini .

formel musik
sumber
1
Pendek dan manis! Jika Anda tidak menyukai busvariabel global , Anda dapat melangkah lebih jauh dan menyuntikkan bus ke komponen Anda menggunakan alat peraga . Saya relatif baru untuk menjawab, jadi saya tidak dapat meyakinkan Anda bahwa ini idiom.
byxor
31

Anda dapat menggunakan sistem acara Vue

vm.$broadcast('event-name', args)

dan

 vm.$on('event-name', function())

Ini biola: http://jsfiddle.net/hfalucas/wc1gg5v4/59/

Helder Lucas
sumber
5
@GusDeCooL contohnya telah diedit. Bukan berarti setelah Vuejs 2.0 beberapa metode yang digunakan di sana telah ditinggalkan
Helder Lucas
1
Berfungsi dengan baik jika hanya ada 1 instance komponen, tetapi jika ada banyak instance, bekerja lebih baik gunakan $ refs.component.method ()
Koronos
22

Versi yang sedikit berbeda (lebih sederhana) dari jawaban yang diterima:

Minta komponen terdaftar pada instance induk:

export default {
    components: { 'my-component': myComponent }
}

Render komponen dalam template / html dengan referensi:

<my-component ref="foo"></my-component>

Akses metode komponen:

<script>
    this.$refs.foo.doSomething();
</script>
Pemenang
sumber
22

Anda dapat mengatur referensi untuk komponen anak, lalu di induk dapat menelepon melalui $ referensi:

Tambahkan referensi ke komponen anak:

<my-component ref="childref"></my-component>

Tambahkan acara klik ke induk:

<button id="external-button" @click="$refs.childref.increaseCount()">External Button</button>

var vm = new Vue({
  el: '#app',
  components: {
    'my-component': { 
      template: '#my-template',
      data: function() {
        return {
          count: 1,
        };
      },
      methods: {
        increaseCount: function() {
          this.count++;
        }
      }
    },
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  
  <my-component ref="childref"></my-component>
  <button id="external-button" @click="$refs.childref.increaseCount()">External Button</button>
</div>
  
<template id="my-template">
  <div style="border: 1px solid; padding: 2px;" ref="childref">
    <p>A counter: {{ count }}</p>
    <button @click="increaseCount">Internal Button</button>
  </div>
</template>

Cong Nguyen
sumber
2
solusi terbersih sejauh ini.
grreeenn
Jawaban yang bagus Saya hanya akan mengedit html menjadi sedikit lebih kecil secara vertikal. Saat ini saya hanya bisa melihat 'Tombol Internal' ketika saya menjalankannya yang bisa membingungkan.
Jesse Reza Khorasanee
8

Katakanlah Anda memiliki child_method()komponen anak:

export default {
    methods: {
        child_method () {
            console.log('I got clicked')
        }
    }
}

Sekarang Anda ingin menjalankan child_methodkomponen dari induk:

<template>
    <div>
        <button @click="exec">Execute child component</button>
        <child-cmp ref="child"></child_cmp> <!-- note the ref="child" here -->
    </div>
</template>

export default {
    methods: {
        exec () { //accessing the child component instance through $refs
            this.$refs.child.child_method() //execute the method belongs to the child component
        }
    }
}

Jika Anda ingin menjalankan metode komponen induk dari komponen turunan:

this.$parent.name_of_method()

CATATAN: Tidak disarankan untuk mengakses komponen anak dan orang tua seperti ini.

Alih-alih sebagai praktik terbaik gunakan Alat Peraga & Acara untuk komunikasi orang tua-anak.

Jika Anda ingin komunikasi antar komponen pasti menggunakan vuex atau event bus

Silakan baca artikel yang sangat membantu ini


roli roli
sumber
Ya, Anda bisa, tetapi tidak dianggap sebagai 'praktik terbaik': ke bawah ke properti penggunaan anak, ke atas ke acara penggunaan orang tua. Untuk juga membahas 'sidewards', gunakan acara khusus, atau mis. Vuex. Lihat artikel yang bagus ini untuk info lebih lanjut.
musicformellons
Ya, tidak disarankan untuk melakukannya.
roli roli
3

Ini adalah cara sederhana untuk mengakses metode komponen dari komponen lain

// This is external shared (reusable) component, so you can call its methods from other components

export default {
   name: 'SharedBase',
   methods: {
      fetchLocalData: function(module, page){
          // .....fetches some data
          return { jsonData }
      }
   }
}

// This is your component where you can call SharedBased component's method(s)
import SharedBase from '[your path to component]';
var sections = [];

export default {
   name: 'History',
   created: function(){
       this.sections = SharedBase.methods['fetchLocalData']('intro', 'history');
   }
}
Dotnetgang
sumber
1

Ini yang sederhana

this.$children[indexOfComponent].childsMethodName();
Pratik Khadtale
sumber
1

Saya tidak yakin apakah ini cara yang benar tetapi ini bekerja untuk saya.
Pertama-tama impor komponen yang berisi metode yang ingin Anda panggil di komponen Anda

import myComponent from './MyComponent'

dan kemudian memanggil metode MyCompenent

myComponent.methods.doSomething()
Rohit Kumar
sumber
ini tidak memberi Anda akses ke data apa pun di komponen Anda. jika Anda doSomethingmenggunakan sesuatu dari data, metode ini tidak berguna.
cyboashu
-7

Saya telah menggunakan solusi yang sangat sederhana. Saya telah memasukkan elemen HTML, yang memanggil metode, dalam Komponen Vue yang saya pilih, menggunakan Vanilla JS, dan saya memicu klik!

Dalam Komponen Vue, saya telah memasukkan sesuatu seperti berikut:

<span data-id="btnReload" @click="fetchTaskList()"><i class="fa fa-refresh"></i></span>

Yang saya gunakan menggunakan Vanilla JS:

const btnReload = document.querySelector('[data-id="btnReload"]');
btnReload.click();                
Gorbas
sumber
Ini tidak dianggap praktik yang baik sama sekali dalam vue, terutama dalam konteks pertanyaan OP.
Web hybrid dev