Cómo llamar a la función en el componente secundario en eventos primarios

164

Contexto

En Vue 2.0, la documentación y otros indican claramente que la comunicación de padres a hijos se realiza a través de accesorios.

Pregunta

¿Cómo le dice un padre a su hijo que un evento ha sucedido a través de accesorios?

¿Debo ver un accesorio llamado evento? Eso no se siente bien, ni las alternativas ( $emit/ $ones para hijo a padre, y un modelo central es para elementos distantes).

Ejemplo

Tengo un contenedor principal y necesita decirle a su contenedor secundario que está bien comprometer ciertas acciones en una API. Necesito poder activar funciones.

jbodily
fuente

Respuestas:

234

Dele al componente hijo ay refuse $refspara llamar a un método en el componente hijo directamente.

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

Para obtener más información, consulte la documentación de Vue en las referencias .

joerick
fuente
13
De esta manera, los componentes padre e hijo se acoplan. Para eventos reales, digamos que cuando no puedes cambiar un accesorio para desencadenar una acción, iría con la solución de autobús sugerida por @Roy J
Jared el
3
una referencia a los documentos también sería útil vuejs.org/v2/guide/…
ctf0
En mi componente hijo, por razones especiales, tuve que usar v-once para terminar la reactividad. Por lo tanto, pasar el accesorio de padres a hijos no era una opción, por lo que esta solución funcionó.
John
1
pregunta para novatos: ¿Por qué usar en reflugar de crear un accesorio, que mira su valor y luego lo emite a otra función en padre? Quiero decir que tiene muchas cosas que hacer, pero ¿es refseguro usarlo ? Gracias
Irfandy Jip
55
@IrfandyJip: sí, refes seguro. En general, se desaconseja porque la comunidad Vue prefiere pasar el estado a los niños y los eventos a los padres. En términos generales, esto conduce a componentes más aislados y consistentes internamente (algo bueno ™). Pero, si la información que le pasa al niño realmente es un evento (o un comando), modificar el estado no es el patrón correcto. En ese caso, llamar a un método usando a refestá totalmente bien, y no se bloqueará ni nada.
joerick
76

Lo que está describiendo es un cambio de estado en el padre. Se lo pasas al niño a través de un accesorio. Como sugirió, sería watchese accesorio. Cuando el niño toma medidas, notifica al padre a través de un emit, y el padre puede cambiar el estado nuevamente.

Si realmente desea pasar eventos a un niño, puede hacerlo creando un autobús (que es solo una instancia de Vue) y pasándolo al niño como accesorio .

Roy J
fuente
2
Creo que esta es la única respuesta en línea con la guía de estilo oficial Vue.JS y las mejores prácticas. Si usa la taquigrafía v-modelen el componente, también puede restablecer fácilmente el valor emitiendo el evento correspondiente con menos código.
Falco
Por ejemplo, quiero dar una alerta cuando un usuario hace clic en un botón. Propone, por ejemplo: - ver una bandera - establecer esta bandera de 0 a 1 cuando se produce un clic, - hacer algo - restablecer bandera
Sinan Erdem
9
Es muy incómodo, tiene que crear un extra propen un niño, una propiedad adicional en data, luego agregar watch... Sería cómodo si hubiera un soporte incorporado para transferir de alguna manera los eventos de padre a hijo. Esta situación ocurre con bastante frecuencia.
Илья Зеленько
1
Según el estado de @ ИльяЗеленько, sucede con bastante frecuencia, sería una bendición ahora mismo.
Craig
1
Gracias @RoyJ, supongo que eso requiere que el accesorio del autobús exista cuando el niño se suscribe, supongo que la idea de enviar eventos a los niños se desaconseja en Vue.
Ben Winding
35

Puedes usar $emity $on. Usando el código @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);
    }
  }
})

Ejemplo de ejecución: https://jsfiddle.net/rjurado/m2spy60r/1/

drinor
fuente
66
Me sorprende que funcione. Pensé que emitir a un niño era un antipatrón, o que la intención era que la emisión fuera solo de niño a padre. ¿Hay algún problema potencial para ir hacia otro lado?
jbodily
2
Puede que no se considere la mejor manera, no lo sé, pero si sabes lo que estás haciendo, creo que no hay problema. La otra forma es usar el bus central: vuejs.org/v2/guide/…
drinor el
14
Esto crea un acoplamiento entre el niño y los padres y se considera una mala práctica
Morrislaptop
55
Esto solo funciona porque el padre no es un componente sino una aplicación vue. En realidad, esto está utilizando la instancia vue como un bus.
Julio Rodrigues
2
@Bsienn la llamada a esto. $ Parent hace que este componente dependa del padre. usa $ emit to y props, por lo que las únicas dependencias son a través del sistema de comunicación de Vue. Este enfoque permite utilizar el mismo componente en cualquier lugar de la jerarquía de componentes.
morrislaptop
6

Si tiene tiempo, use la tienda Vuex para ver variables (también conocido como estado) o desencadenar (también conocido como despachar) una acción directamente.

brightknight08
fuente
2
debido a la reactividad de vuejs / vuex que es el mejor enfoque, en el padre realiza una acción / mutación que cambia el valor de una propiedad vuex y en el niño tiene un valor calculado que obtiene este mismo vuex $ store.state.property.value o un "watch "método que hace algo cuando vuex" $ store.state.property.value "cambia
FabianSilva
6

No me gustó el enfoque del bus de eventos usando $onenlaces en el niño durante create. ¿Por qué? createLlamadas posteriores (estoy usandovue-router ) vinculan el controlador de mensajes más de una vez, lo que genera múltiples respuestas por mensaje.

La solución ortodoxa de pasar los accesorios de padres a hijos y poner un vigilante en el niño funcionó un poco mejor. El único problema es que el niño solo puede actuar en una transición de valor. Pasar el mismo mensaje varias veces necesita algún tipo de contabilidad para forzar una transición para que el niño pueda recoger el cambio.

Descubrí que si envuelvo el mensaje en una matriz, siempre activará el observador secundario, incluso si el valor sigue siendo el mismo.

Padre:

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

Niño:

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

HTML:

<parent>
   <child v-bind="{ 'msgChild': msgChild }"></child>
</parent>
Jason Stewart
fuente
1
Creo que esto no funcionará si msgChild siempre tiene el mismo estado en el padre. Por ejemplo: quiero un componente que abra un modal. Al padre no le importa si el estado actual es abierto o cerrado, solo quiere abrir el modal en cualquier momento. Entonces, si el padre hace esto.msgChild = true; el modal está cerrado, y luego el padre hace esto.msgChild = true, el niño no recibirá el evento
Jorge Sainz
1
@JorgeSainz: Es por eso que estoy envolviendo el valor en una matriz antes de asignarlo al elemento de datos. Sin ajustar el valor en una matriz, se comporta tal como lo especifica. Entonces, msgChild = true, msgChild = true - sin evento. msgChild = [verdadero], msgChild = [verdadero] - evento!
Jason Stewart el
1
No lo vi Gracias por la aclaración
Jorge Sainz
Esto es genial, pero se siente un poco hackear. Lo voy a usar, ya que es más limpio que usar el hack de referencia de componentes y menos complicado que la solución de bus de eventos. Sé que vue quiere desacoplarse y solo permitir que los cambios de estado afecten al componente, pero debería haber alguna forma integrada de llamar a los métodos de un niño si es necesario. Tal vez un modificador en un accesorio que una vez que cambia de estado, podría restablecerlo automáticamente a un valor predeterminado para que el observador esté listo para el próximo cambio de estado. De todos modos, gracias por publicar tu hallazgo.
Craig
6

Una forma simple de desacoplar los métodos de llamada en componentes secundarios es emitir un controlador desde el elemento secundario y luego invocarlo desde el elemento primario.

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>

El padre realiza un seguimiento de las funciones y llamadas del controlador secundario siempre que sea necesario.

nilobarp
fuente
Me gusta a dónde va esta solución, pero ¿qué es exactamente "this.setter" en el padre?
Craig
Es la referencia de función setValue emitida por el componente hijo como argumento para el evento controlador.
nilobarp
2

El siguiente ejemplo es auto explicativo. donde las referencias y los eventos se pueden utilizar para llamar a la función desde y hacia padre e hijo.

// 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
fuente
1

Creo que deberíamos tener en cuenta la necesidad de que los padres usen los métodos del niño. De hecho, los padres no deben preocuparse por el método del niño, sino que pueden tratar el componente infantil como una FSA (máquina de estados finitos). para controlar el estado del componente secundario. Por lo tanto, la solución para observar el cambio de estado o simplemente usar la función de cálculo es suficiente

usuario10097040
fuente
2
Si está diciendo que "los padres nunca deberían preocuparse por controlar a los niños", hay casos en que esto es necesario. Considere un componente de temporizador de cuenta regresiva. El padre puede desear restablecer el temporizador para comenzar de nuevo. Simplemente usar accesorios no es suficiente porque pasar de tiempo = 60 a tiempo = 60 no modificará el accesorio. El temporizador debe exponer una función de 'reinicio' que el padre puede llamar según corresponda.
tbm
0

puede usar la clave para recargar el componente secundario usando la clave

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

Si desea volver a cargar el componente con un nuevo filtro, si hace clic en el botón, filtre el componente secundario

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

y usa el filtro para activar la función

Ardian Zack
fuente