Cómo acceder a un método secundario desde el padre en vue.js

91

Tengo dos componentes anidados, ¿cuál es la forma correcta de acceder a los métodos secundarios del padre?

this.$children[0].myMethod() parece hacer el truco, pero es bastante feo, ¿no? ¿Qué puede ser mejor?

<script>
import child from './my-child'

export default {
  components: {
   child
  },
  mounted () {
    this.$children[0].myMethod()
  }
}
</script>
al3x
fuente
Primero, pregúntese si realmente lo necesita. Si todo el estado de su página está en una tienda, como debería estar, no hay necesidad de comunicación entre padres e hijos.
bbsimonbb
7
@bbsimonbb El estado es diferente a los eventos. Se trata específicamente de desencadenar eventos secundarios de los padres. También puede hacer lo que sea para lo que usaría Vuex pasando un accesorio en sentido descendente, pero esto requiere que el componente secundario observe el accesorio / almacén en busca de cambios para que pueda emular efectivamente RPC con cambios de datos, lo cual es simplemente incorrecto cuando todo lo que desea es desencadenar una acción en el componente.
Bojan Markovic

Respuestas:

244

Puede utilizar ref .

import ChildForm from './components/ChildForm'

new Vue({
  el: '#app',
  data: {
    item: {}
  },
  template: `
  <div>
     <ChildForm :item="item" ref="form" />
     <button type="submit" @click.prevent="submit">Post</button>
  </div>
  `,
  methods: {
    submit() {
      this.$refs.form.submit()
    }
  },
  components: { ChildForm },
})

Si no le gusta el acoplamiento estrecho, puede usar Event Bus como lo muestra @Yosvel Quintero. A continuación se muestra otro ejemplo de uso de bus de eventos pasando el bus como accesorios.

import ChildForm from './components/ChildForm'

new Vue({
  el: '#app',
  data: {
    item: {},
    bus: new Vue(),
  },
  template: `
  <div>
     <ChildForm :item="item" :bus="bus" ref="form" />
     <button type="submit" @click.prevent="submit">Post</button>
  </div>
  `,
  methods: {
    submit() {
      this.bus.$emit('submit')
    }
  },
  components: { ChildForm },
})

Código de componente.

<template>
 ...
</template>

<script>
export default {
  name: 'NowForm',
  props: ['item', 'bus'],
  methods: {
    submit() {
        ...
    }
  },
  mounted() {
    this.bus.$on('submit', this.submit)
  },  
}
</script>

https://code.luasoftware.com/tutorials/vuejs/parent-call-child-component-method/

Desmond Lua
fuente
38
Esta es la respuesta correcta, que realmente lee la pregunta real. La respuesta seleccionada en realidad responde a la pregunta opuesta (cómo activar un método en el componente principal desde el componente secundario).
Bojan Markovic
1
El enlace para Event Bus que está vinculando esta respuesta, se redirige a State Management , después de leer el comentario de @bbsimonbb , tiene sentido.
Eido95
2
Vale la pena mencionar que si lo usa this.$refs., no debe cargar el componente secundario de forma dinámica.
1_bug
¡gracias Señor! Me ahorraste muchos problemas. ¡Estaba solucionando un problema de producción y estaba buscando respuestas desesperadamente! <3
Osama Ibrahim
27

Comunicación entre padres e hijos en VueJS

Dado que todos los descendientes this.$rootpueden acceder a una instancia raíz de Vue , un componente principal puede acceder a los componentes secundarios a través de la this.$childrenmatriz, y un componente secundario puede acceder a su principalthis.$parent , su primer instinto podría ser acceder a estos componentes directamente.

La documentación de VueJS advierte contra esto específicamente por dos muy buenas razones:

  • Acopla estrechamente al padre con el hijo (y viceversa)
  • No puede confiar en el estado del padre, dado que un componente hijo puede modificarlo.

La solución es utilizar la interfaz de eventos personalizada de Vue

La interfaz de eventos implementada por Vue le permite comunicarse hacia arriba y hacia abajo en el árbol de componentes. Aprovechar la interfaz de eventos personalizados le brinda acceso a cuatro métodos:

  1. $on() - le permite declarar un oyente en su instancia de Vue con el que escuchar eventos
  2. $emit() - le permite activar eventos en la misma instancia (auto)

Ejemplo usando $on()y $emit():

const events = new Vue({}),
    parentComponent = new Vue({
      el: '#parent',
      ready() {
        events.$on('eventGreet', () => {
          this.parentMsg = `I heard the greeting event from Child component ${++this.counter} times..`;
        });
      },
      data: {
        parentMsg: 'I am listening for an event..',
        counter: 0
      }
    }),
    childComponent = new Vue({
      el: '#child',
      methods: {
      greet: function () {
        events.$emit('eventGreet');
        this.childMsg = `I am firing greeting event ${++this.counter} times..`;
      }
    },
    data: {
      childMsg: 'I am getting ready to fire an event.',
      counter: 0
    }
  });
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.28/vue.min.js"></script>

<div id="parent">
  <h2>Parent Component</h2>
  <p>{{parentMsg}}</p>
</div>

<div id="child">
  <h2>Child Component</h2>
  <p>{{childMsg}}</p>
  <button v-on:click="greet">Greet</button>
</div>

Respuesta tomada de la publicación original: Comunicación entre componentes en VueJS

Yosvel Quintero Argüelles
fuente
1
Gracias, ¡intentaré mutualizar mi código a través de eventos!
al3x
5
al copiar / pegar cosas, es bueno mencionar también la fuente.
Mihai Vilcu
17
Esto es útil en la comunicación de un niño a un padre. Pero, ¿existe una forma similar de hacerlo de padres a hijos? Por ejemplo, antes de permitir que el usuario agregue un nuevo niño, quiero que todos los existentes sean validados; la lógica de validación está en el niño, así que quiero revisarlos todos y ejecutar, por ejemplo, el método validate ().
Mateusz Bartkowiak
66
Esto responde a la pregunta opuesta a lo que realmente se preguntó. La respuesta de Desmond Lua responde a la pregunta real.
Bojan Markovic
4
Event bus es el número 1 en la lista de antipatrones vue de Chris Fritz . Todo lo que se pueda modelar con eventos y estado distribuido se puede modelar con estado global y enlace bidireccional, y en general estará mucho mejor.
bbsimonbb
1

Ref y event bus tienen problemas cuando su renderizado de control se ve afectado por v-if. Entonces, decidí ir con un método más simple.

La idea es usar una matriz como cola para enviar métodos que deben llamarse al componente secundario. Una vez que se montó el componente, procesará esta cola. Observa la cola para ejecutar nuevos métodos.

(Tomando prestado un código de la respuesta de Desmond Lua)

Código del componente principal:

import ChildComponent from './components/ChildComponent'

new Vue({
  el: '#app',
  data: {
    item: {},
    childMethodsQueue: [],
  },
  template: `
  <div>
     <ChildComponent :item="item" :methods-queue="childMethodsQueue" />
     <button type="submit" @click.prevent="submit">Post</button>
  </div>
  `,
  methods: {
    submit() {
      this.childMethodsQueue.push({name: ChildComponent.methods.save.name, params: {}})
    }
  },
  components: { ChildComponent },
})

Este es el código para ChildComponent

<template>
 ...
</template>

<script>
export default {
  name: 'ChildComponent',
  props: {
    methodsQueue: { type: Array },
  },
  watch: {
    methodsQueue: function () {
      this.processMethodsQueue()
    },
  },
  mounted() {
    this.processMethodsQueue()
  },
  methods: {
    save() {
        console.log("Child saved...")
    },
    processMethodsQueue() {
      if (!this.methodsQueue) return
      let len = this.methodsQueue.length
      for (let i = 0; i < len; i++) {
        let method = this.methodsQueue.shift()
        this[method.name](method.params)
      }
    },
  },
}
</script>

Y hay mucho margen de mejora, como pasar processMethodsQueuea un mixin ...

mohghaderi
fuente
0

Para comunicar un componente secundario con otro componente secundario, he creado un método en padre que llama a un método en un niño con:

this.$refs.childMethod()

Y del otro niño llamé al método raíz:

this.$root.theRootMethod()

Funcionó para mí.

Jonathan Arias
fuente
Esta respuesta carece de mucha explicación
vsync