Chiamare un metodo componente Vue.js dall'esterno del componente


229

Diciamo che ho un'istanza principale di Vue con componenti figlio. Esiste un modo per chiamare un metodo appartenente a uno di questi componenti al di fuori dell'istanza di Vue?

Ecco un esempio:

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>

Quindi, quando faccio clic sul pulsante interno, il increaseCount()metodo è associato all'evento click, quindi viene chiamato. Non c'è modo di associare l'evento al pulsante esterno, il cui evento click sto ascoltando con jQuery, quindi avrò bisogno di un altro modo per chiamare increaseCount.

MODIFICARE

Sembra che funzioni:

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

Tuttavia, questa non è una buona soluzione perché sto facendo riferimento al componente tramite il suo indice nell'array figlio e con molti componenti è improbabile che rimanga costante e che il codice sia meno leggibile.


1
Ho aggiunto una risposta usando mxins se vuoi provarlo. Secondo me preferisco configurare l'app in questo modo.
Omar Tanti,

Risposte:


275

Alla fine ho optato per l'uso della refdirettiva di Vue . Ciò consente a un componente di fare riferimento al genitore per l'accesso diretto.

Per esempio

Ho un componente registrato sull'istanza del mio genitore:

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

Rendering del componente in template / html con un riferimento:

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

Ora, altrove, posso accedere al componente esternamente

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

Vedi questo violino per un esempio: https://jsfiddle.net/xmqgnbu3/1/

(vecchio esempio usando Vue 1: https://jsfiddle.net/6v7y6msr/ )


6
Questo perché probabilmente non l'hai definito. Guarda il violino collegato.
harryg

29
Se si utilizza il webpack, non sarà possibile accedervi a vmcausa del modo in cui i moduli di ambito. Puoi fare qualcosa di simile window.app = vmnel tuo main.js. Fonte: forum.vuejs.org/t/how-to-access-vue-from-chrome-console/3606
tw

1
Questo approccio può essere considerato normale? O è un trucco?
CENT1PEDE,

3
C'è così una definizione ufficiale di cos'è un hack rispetto a ciò che è una codifica "normale", ma piuttosto che chiamare questo approccio un hack (o cercare un modo "meno confuso" per ottenere la stessa cosa) è probabilmente meglio chiedersi perché è necessario Questo. In molti casi potrebbe essere più elegante utilizzare il sistema di eventi di Vue per innescare il comportamento di un componente esterno, o anche chiedersi perché si stanno attivando i componenti esternamente.
harryg,

8
Questo può apparire se stai cercando di integrare un componente di visualizzazione in una pagina già esistente. Invece di ridisegnare completamente la pagina, a volte è bello aggiungere in modo incrementale funzionalità aggiuntive.
Allen Wang,

37

Da Vue2 questo vale:

var bus = new Vue()

// nel metodo del componente A.

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

// nell'hook creato dal componente B.

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

  // ...
})

Vedi qui per i documenti Vue. Ed ecco maggiori dettagli su come impostare esattamente questo bus eventi.

Se desideri maggiori informazioni su quando utilizzare proprietà, eventi e / o gestione centralizzata dello stato, consulta questo articolo .


1
Breve e dolce! Se non ti piace la busvariabile globale , puoi fare un passo ulteriore e iniettare il bus nel tuo componente usando i puntelli . Sono relativamente nuovo per me, quindi non posso assicurarti che sia idiomatico.
byxor,

31

È possibile utilizzare il sistema di eventi Vue

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

e

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

Ecco il violino: http://jsfiddle.net/hfalucas/wc1gg5v4/59/


5
@GusDeCooL l'esempio è stato modificato. Non che dopo Vuejs 2.0 alcuni metodi usati siano stati deprecati
Helder Lucas l'

1
Funziona bene se esiste solo 1 istanza del componente, ma se ci sono molte istanze, funziona meglio usando $ refs.component.method ()
Koronos il

22

Una versione leggermente diversa (più semplice) della risposta accettata:

Avere un componente registrato nell'istanza padre:

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

Rendering del componente in template / html con un riferimento:

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

Accedi al metodo del componente:

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

22

È possibile impostare ref per i componenti figlio, quindi in parent può chiamare tramite $ refs:

Aggiungi riferimento al componente figlio:

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

Aggiungi evento click al genitore:

<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>


2
la soluzione più pulita finora.
grreeenn,

Bella risposta. Ho intenzione di modificare l'html per essere un po 'più piccolo in verticale. Al momento riesco a vedere 'Pulsante interno' solo quando lo eseguo, il che può essere fonte di confusione.
Jesse Reza Khorasanee,

8

Supponi di avere un child_method()componente secondario:

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

Ora vuoi eseguire il child_methodcomponente from parent:

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

Se si desidera eseguire un metodo del componente padre dal componente figlio:

this.$parent.name_of_method()

NOTA: non è consigliabile accedere al componente figlio e genitore in questo modo.

Invece come best practice usa Props & Events per la comunicazione genitore-figlio.

Se vuoi comunicare tra i componenti sicuramente usa vuex o il bus degli eventi

Si prega di leggere questo articolo molto utile



Sì, puoi, ma non considerato "best practice": verso il basso per le proprietà di utilizzo Child, verso l'alto per gli eventi di utilizzo Parent. Per coprire anche "lateralmente", utilizzare eventi personalizzati o ad esempio vuex. Vedi questo simpatico articolo per maggiori informazioni.
musicformellons

Sì, non è consigliabile farlo.
roli roli,

3

Questo è un modo semplice per accedere ai metodi di un componente da un altro componente

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


1

Non sono sicuro sia nel modo giusto, ma questo funziona per me.
In primo luogo importare il componente che contiene il metodo che si desidera chiamare nel componente

import myComponent from './MyComponent'

e quindi chiamare qualsiasi metodo di MyCompenent

myComponent.methods.doSomething()

questo non ti dà accesso a nessun dato nel tuo componente. se stai doSomethingusando qualcosa dai dati, questo metodo è inutile.
cyboashu,

-7

Ho usato una soluzione molto semplice. Ho incluso un elemento HTML, che chiama il metodo, nel mio componente Vue che seleziono, usando Vanilla JS, e innesco il clic!

Nel componente Vue ho incluso qualcosa di simile al seguente:

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

Che uso usando Vanilla JS:

const btnReload = document.querySelector('[data-id="btnReload"]');
btnReload.click();                

Ciò non è affatto considerato una buona pratica, soprattutto nel contesto della domanda del PO.
Dev web ibrido,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.