Come chiamare la funzione sul componente figlio su eventi padre


164

Contesto

In Vue 2.0 la documentazione e altri indicano chiaramente che la comunicazione da genitore a figlio avviene tramite oggetti di scena.

Domanda

Come fa un genitore a dire a suo figlio che un evento è accaduto tramite oggetti di scena?

Devo solo guardare un oggetto chiamato evento? Ciò non sembra corretto, né esistono alternative ( $emit/ $onè da bambino a genitore e un modello hub è per elementi distanti).

Esempio

Ho un contenitore padre e deve dire al contenitore figlio che va bene eseguire determinate azioni su un'API. Devo essere in grado di attivare le funzioni.

Risposte:


234

Assegnare un componente figlio a refe utilizzare $refsper chiamare direttamente un metodo sul componente figlio.

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

Per ulteriori informazioni, consultare la documentazione di Vue sui riferimenti .


13
In questo modo i componenti padre e figlio vengono accoppiati. Per eventi reali, diciamo che quando non si può semplicemente cambiare un puntello per innescare un'azione, andrei con la soluzione di autobus suggerita da @Roy J
Jared

3
un riferimento ai documenti sarebbe utile anche vuejs.org/v2/guide/…
ctf0

Nel mio componente figlio, per motivi speciali, ho dovuto usare v-once per terminare la reattività. Quindi passare l'elica da genitore a figlio non era un'opzione, quindi questa soluzione ha funzionato!
Giovanni,

1
domanda per principianti: Perché usare refinvece di creare un oggetto di scena, che osserva il suo valore e poi lo emette in un'altra funzione genitore? Voglio dire, ha molte cose da fare, ma l'utilizzo è refpersino sicuro? Grazie
Irfandy Jip,

5
@IrfandyJip - sì, refè sicuro. In generale, è scoraggiato perché la comunità Vue preferisce passare lo stato ai bambini e gli eventi al genitore. In generale, ciò porta a componenti più isolati e coerenti internamente (una buona cosa ™). Ma, se le informazioni che stai passando al bambino sono davvero un evento (o un comando), la modifica dello stato non è il modello giusto. In tal caso, chiamare un metodo usando un refva benissimo e non andrà in crash o altro.
Joerick,

76

Quello che stai descrivendo è un cambio di stato nel genitore. Lo passi al bambino tramite un sostegno. Come hai suggerito, faresti watchquel prop. Quando il bambino interviene, avvisa il genitore tramite un emite il genitore potrebbe quindi cambiare di nuovo lo stato.

Se vuoi veramente passare eventi a un bambino, puoi farlo creando un bus (che è solo un'istanza di Vue) e passandolo al bambino come prop .


2
Penso che questa sia l'unica risposta in linea con la guida di stile ufficiale Vue.JS e le migliori pratiche. Se si utilizza la scorciatoia v-modelsul componente, è anche possibile ripristinare facilmente il valore emettendo l'evento corrispondente con meno codice.
Falco,

Ad esempio, voglio dare un avviso quando un utente fa clic su un pulsante. Proponi ad esempio: - guarda un flag - imposta questo flag da 0 a 1 quando si verifica un clic, - fai qualcosa - resetta flag
Sinan Erdem

9
È molto scomodo, devi creare un extra propin un bambino, una proprietà extra in data, quindi aggiungere watch... Sarebbe comodo se ci fosse un supporto integrato per trasferire in qualche modo eventi da genitore a figlio. Questa situazione si verifica abbastanza spesso.
Илья Зеленько,

1
Come dichiarato da @ ИльяЗеленько, succede abbastanza spesso, sarebbe una manna dal cielo in questo momento.
Craig,

1
Grazie @RoyJ, suppongo che richieda l'esistenza del sostegno del bus quando il bambino si abbona ad esso, suppongo che l'idea di inviare eventi ai bambini sia scoraggiata a Vue.
Ben Winding,

35

Puoi usare $emite $on. Utilizzando il codice @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);
    }
  }
})

Esempio corrente: https://jsfiddle.net/rjurado/m2spy60r/1/


6
Sono sorpreso che funzioni. Pensavo che emettere a un bambino fosse un anti-schema, o che l'intenzione era che l'emissione fosse solo da bambino a genitore. Ci sono potenziali problemi nell'andare dall'altra parte?
jbodily,

2
Questo non può essere considerato il modo migliore, non lo so, ma se sai cosa stai facendo, allora non è un problema. L'altro modo è utilizzare l'autobus centrale: vuejs.org/v2/guide/…
drinor

14
Questo crea un accoppiamento tra bambino e genitore ed è considerato una cattiva pratica
morrislaptop,

5
Funziona solo perché il genitore non è un componente ma in realtà un'app vue. In realtà questo utilizza l'istanza vue come bus.
Julio Rodrigues,

2
@Bsienn la chiamata a questo. $ Parent rende questo componente dipendente dal genitore. usa $ emit to e props quindi le uniche dipendenze sono attraverso il sistema di comunicazione di Vue. Questo approccio consente di utilizzare lo stesso componente ovunque nella gerarchia dei componenti.
morrislaptop,

6

Se hai tempo, usa Vuex store per guardare le variabili (aka state) o attivare (aka dispatch) un'azione direttamente.


2
a causa della reattività di vuejs / vuex che è l'approccio migliore, in parent eseguono un'azione / mutazione che modifica un valore della proprietà vuex e in child ha un valore calcolato che ottiene lo stesso vuex $ store.state.property.value o un "watch "metodo che fa qualcosa quando vuex" $ store.state.property.value "cambia
FabianSilva,

6

Non mi è piaciuto l' approccio del bus degli eventi usando le $onassociazioni nel bambino durante create. Perché? Le createchiamate successive (che sto usando vue-router) associano il gestore messaggi più di una volta, portando a più risposte per messaggio.

La soluzione ortodossa di tramandare gli oggetti di scena da un genitore all'altro e di mettere un osservatore di proprietà nel bambino ha funzionato un po ' meglio. L'unico problema è che il bambino può agire solo su una transizione di valore. Passare lo stesso messaggio più volte richiede una sorta di contabilità per forzare una transizione in modo che il bambino possa prendere la modifica.

Ho scoperto che se avvolgo il messaggio in un array, attiverà sempre il watcher figlio, anche se il valore rimane lo stesso.

Genitore:

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

Bambino:

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

HTML:

<parent>
   <child v-bind="{ 'msgChild': msgChild }"></child>
</parent>

1
Penso che questo non funzionerà se msgChild ha sempre lo stesso stato sul genitore. Ad esempio: voglio un componente che apre un modale. Al genitore non importa se lo stato corrente è aperto o chiuso, vuole solo aprire il modale in qualsiasi momento. Quindi, se il genitore fa this.msgChild = true; il modale viene chiuso e quindi il genitore fa this.msgChild = true, il figlio non riceverà l'evento
Jorge Sainz,

1
@JorgeSainz: ecco perché sto avvolgendo il valore in un array prima di assegnarlo all'elemento dati. Senza racchiudere il valore in un array, si comporta esattamente come specificato. Quindi, msgChild = true, msgChild = true - nessun evento. msgChild = [true], msgChild = [true] - evento!
Jason Stewart,

1
Non l'ho visto Grazie per il chiarimento
Jorge Sainz,

Questo è bello, ma sembra un po 'hacker. Lo userò dal momento che è più pulito dell'uso dell'hacking dei componenti e meno complicato della soluzione del bus degli eventi. So che Vue vuole disaccoppiare e consentire solo cambiamenti di stato per avere effetto sul componente, ma ci dovrebbe essere un modo incorporato per chiamare i metodi di un bambino se necessario. Forse un modificatore su un oggetto di scena che una volta cambiato stato potresti ripristinarlo automaticamente su un valore predefinito in modo che l'osservatore sia pronto per il prossimo cambio di stato. Comunque grazie per aver pubblicato la tua ricerca.
Craig,

6

Un semplice modo disaccoppiato per chiamare metodi su componenti figlio consiste nell'emettere un gestore dal figlio e quindi invocarlo dal genitore.

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/vue@2.5.17/dist/vue.js"></script>

<div id="app">
  <my-component @handler="setValueHandler"></my-component>
  <button @click="click">Click</button>  
</div>

Il genitore tiene traccia delle funzioni del gestore figlio e chiama quando necessario.


Mi piace dove sta andando questa soluzione, ma cos'è esattamente "this.setter" nel genitore?
Craig,

È il riferimento della funzione setValue emesso dal componente figlio come argomento all'evento gestore.
nilobarp,

2

L'esempio seguente è autoesplicativo. dove ref ed eventi possono essere usati per chiamare la funzione da e verso genitore e figlio.

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

1

Penso che dovremmo prendere in considerazione la necessità del genitore di usare i metodi del bambino, infatti i genitori non devono preoccuparsi del metodo del bambino, ma possono trattare il componente figlio come un FSA (macchina a stati finiti). per controllare lo stato del componente figlio. Quindi è sufficiente la soluzione per guardare il cambiamento di stato o semplicemente usare la funzione di calcolo


2
Se stai dicendo "i genitori non dovrebbero mai preoccuparsi di controllare i figli", ci sono casi in cui ciò è necessario. Considera un componente timer per il conto alla rovescia. Il genitore potrebbe voler ripristinare il timer per ricominciare. Semplicemente usare gli oggetti di scena non è sufficiente perché passare da time = 60 a time = 60 non modificherà l'elica. Il timer dovrebbe esporre una funzione di "reset" che il genitore può chiamare come appropriato.
martedì

0

puoi usare key per ricaricare il componente figlio usando key

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

Se si desidera ricaricare il componente con un nuovo filtro, se si fa clic sul pulsante, filtrare il componente figlio

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

e utilizzare il filtro per attivare la funzione

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.