Llamar a un método de componente Vue.js desde fuera del componente

229

Digamos que tengo una instancia principal de Vue que tiene componentes secundarios. ¿Hay alguna manera de llamar a un método que pertenece a uno de estos componentes desde fuera de la instancia de Vue por completo?

Aquí hay un ejemplo:

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>

Entonces, cuando hago clic en el botón interno, el increaseCount()método está vinculado a su evento de clic, por lo que se llama. No hay forma de vincular el evento al botón externo, cuyo evento de clic estoy escuchando con jQuery, por lo que necesitaré alguna otra forma de llamar increaseCount.

EDITAR

Parece que esto funciona:

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

Sin embargo, esta no es una buena solución porque estoy haciendo referencia al componente por su índice en la matriz secundaria, y con muchos componentes es poco probable que se mantenga constante y el código es menos legible.

harryg
fuente
1
Agregué una respuesta usando mxins si quieres probarlo. En mi opinión, prefiero configurar la aplicación de esta manera.
Omar Tanti

Respuestas:

275

Al final, opté por usar la refdirectiva de Vue . Esto permite que un componente sea referenciado desde el padre para acceso directo.

P.ej

Tener un competidor registrado en mi instancia principal:

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

Renderice el componente en template / html con una referencia:

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

Ahora, en otro lugar puedo acceder al componente externamente

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

Vea este violín para ver un ejemplo: https://jsfiddle.net/xmqgnbu3/1/

(antiguo ejemplo con Vue 1: https://jsfiddle.net/6v7y6msr/ )

harryg
fuente
66
Eso es porque probablemente no lo hayas definido. Mira el violín vinculado.
harryg el
29
Si está utilizando webpack, entonces no podrá acceder vmdebido a la forma en que abarca los módulos. Puedes hacer algo como window.app = vmen tu main.js. Fuente: forum.vuejs.org/t/how-to-access-vue-from-chrome-console/3606
tw airball
1
¿Se puede considerar este enfoque como normal? ¿O es esto un truco?
CENT1PEDE
3
Existe una definición oficial de lo que es un hack frente a lo que es la codificación "normal", pero en lugar de llamar a este enfoque un hack (o buscar una forma "menos hacky" de lograr lo mismo), probablemente sea mejor preguntarse por qué necesita hacerlo esta. En muchos casos, podría ser más elegante usar el sistema de eventos de Vue para activar el comportamiento de los componentes externos, o incluso preguntar por qué está activando componentes externamente.
harryg
8
Esto puede aparecer si está intentando integrar un componente de vista en una página ya existente. En lugar de rediseñar completamente la página, a veces es bueno agregar funcionalidades adicionales de forma incremental.
Allen Wang
37

Desde Vue2 esto aplica:

var bus = new Vue()

// en el método del componente A

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

// en el gancho creado del componente B

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

  // ...
})

Vea aquí los documentos de Vue. Y aquí hay más detalles sobre cómo configurar este bus de eventos exactamente.

Si desea obtener más información sobre cuándo usar propiedades, eventos y / o administración de estado centralizada, consulte este artículo .

musicformellons
fuente
1
¡Corto y dulce! Si no le gusta la busvariable global , puede ir un paso más allá e inyectar el bus en su componente utilizando accesorios . Soy relativamente nuevo en vue, así que no puedo asegurarte que esto sea idiomático.
byxor
31

Puedes usar el sistema de eventos Vue

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

y

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

Aquí está el violín: http://jsfiddle.net/hfalucas/wc1gg5v4/59/

Helder Lucas
fuente
55
@GusDeCooL el ejemplo ha sido editado. No es que después de Vuejs 2.0 algunos métodos utilizados hayan quedado en desuso
Helder Lucas
1
Funciona bien si solo hay 1 instancia de componente, pero si hay muchas instancias, funciona mejor con $ refs.component.method ()
Koronos
22

Una versión ligeramente diferente (más simple) de la respuesta aceptada:

Tener un componente registrado en la instancia principal:

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

Renderice el componente en template / html con una referencia:

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

Acceda al método componente:

<script>
    this.$refs.foo.doSomething();
</script>
Víctor
fuente
22

Puede establecer la referencia para los componentes secundarios y luego, en la función principal, puede llamar a través de $ refs:

Añadir referencia al componente hijo:

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

Añadir evento de clic al padre:

<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
fuente
2
La solución más limpia hasta ahora.
grreeenn
Gran respuesta. Solo voy a editar el html para que sea un poco más pequeño verticalmente. Actualmente solo puedo ver 'Botón interno' cuando lo ejecuto, lo que puede ser confuso.
Jesse Reza Khorasanee el
8

Digamos que tiene un child_method()componente secundario:

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

Ahora desea ejecutar child_methoddesde el componente primario:

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

Si desea ejecutar un método de componente primario desde el componente secundario:

this.$parent.name_of_method()

NOTA: No se recomienda acceder al componente secundario y primario de esta manera.

En cambio, como práctica recomendada, use Props & Events para la comunicación entre padres e hijos.

Si desea comunicación entre componentes, seguramente use vuex o bus de eventos

Por favor lea este artículo muy útil


roli roli
fuente
Sí, puede, pero no se considera 'mejor práctica': propiedades de uso descendente a uso infantil, eventos ascendentes a uso principal. Para cubrir también 'hacia los lados', use eventos personalizados o, por ejemplo, vuex. Vea este bonito artículo para más información.
musicformellons
Sí, no se recomienda hacerlo.
roli roli
3

Esta es una manera simple de acceder a los métodos de un componente desde otro 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');
   }
}
Dotnetgang
fuente
1

Aquí hay uno simple

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

No estoy seguro de si es la forma correcta, pero esta me funciona.
Primero importe el componente que contiene el método al que desea llamar en su componente

import myComponent from './MyComponent'

y luego llame a cualquier método de MyCompenent

myComponent.methods.doSomething()
Rohit Kumar
fuente
esto no le da acceso a ningún dato en su componente. Si doSomethingestá utilizando algo de los datos, este método es inútil.
cyboashu
-7

He usado una solución muy simple. ¡He incluido un elemento HTML, que llama al método, en mi Componente Vue que selecciono, usando Vanilla JS, y disparo el clic!

En el componente Vue, he incluido algo como lo siguiente:

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

Que uso usando Vanilla JS:

const btnReload = document.querySelector('[data-id="btnReload"]');
btnReload.click();                
Gorbas
fuente
Esto no se considera una buena práctica en absoluto, especialmente en el contexto de la pregunta de OP.
Desarrollador web híbrido