Comment accéder à une méthode enfant à partir du parent dans vue.js

91

J'ai deux composants imbriqués, quelle est la bonne façon d'accéder aux méthodes enfants à partir du parent?

this.$children[0].myMethod() semble faire l'affaire mais c'est assez moche, n'est-ce pas, quoi de mieux:

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

export default {
  components: {
   child
  },
  mounted () {
    this.$children[0].myMethod()
  }
}
</script>
al3x
la source
Tout d'abord, demandez-vous si vous en avez vraiment besoin. Si tout l'état de votre page est dans un magasin, comme il se doit, il n'y a pas besoin de communication parent-enfant.
bbsimonbb
7
@bbsimonbb State est différent des événements. Il s'agit spécifiquement de déclencher des événements enfants depuis le parent. Vous pouvez également faire tout ce que vous utiliseriez pour Vuex en passant un accessoire en aval, mais cela nécessite que le composant enfant surveille le prop / store pour les modifications afin que vous émuliez efficacement RPC avec des modifications de données, ce qui est tout simplement faux lorsque tout ce que vous voulez, c'est déclencher une action dans le composant.
Bojan Markovic

Réponses:

244

Vous pouvez utiliser la réf .

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 vous n'aimez pas le couplage serré, vous pouvez utiliser Event Bus comme indiqué par @Yosvel Quintero. Voici un autre exemple d'utilisation du bus d'événements en passant dans le bus comme accessoires.

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

Code du composant.

<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
la source
38
C'est la bonne réponse, qui lit réellement la question réelle. La réponse sélectionnée répond en fait à la question opposée (comment déclencher une méthode sur le parent depuis le composant enfant).
Bojan Markovic
1
Le lien pour Event Bus que cette réponse relie, redirige vers State Management , après avoir lu le commentaire @bbsimonbb , c'est un peu logique.
Eido95
2
Cela vaut la peine de mentionner que si vous utilisez this.$refs., vous ne devriez pas charger le composant enfant de manière dynamique.
1_bug
Merci Monsieur! Vous m'avez épargné beaucoup d'ennuis. J'étais en train de résoudre un problème de production et je cherchais désespérément des réponses! <3
Osama Ibrahim
27

Communication parent-enfant dans VueJS

Étant donné qu'une instance racine de Vue est accessible par tous les descendants via this.$root, un composant parent peut accéder aux composants enfants via le this.$childrentableau, et un composant enfant peut accéder à son parent via this.$parent, votre premier instinct peut être d'accéder directement à ces composants.

La documentation VueJS met en garde contre cela spécifiquement pour deux très bonnes raisons:

  • Il couple étroitement le parent à l'enfant (et vice versa)
  • Vous ne pouvez pas vous fier à l'état du parent, étant donné qu'il peut être modifié par un composant enfant.

La solution est d'utiliser l'interface événementielle personnalisée de Vue

L'interface événementielle implémentée par Vue vous permet de communiquer de haut en bas dans l'arborescence des composants. L'utilisation de l'interface d'événement personnalisée vous donne accès à quatre méthodes:

  1. $on() - vous permet de déclarer un auditeur sur votre instance Vue avec lequel écouter les événements
  2. $emit() - vous permet de déclencher des événements sur la même instance (self)

Exemple utilisant $on()et $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>

Réponse tirée du message original: Communiquer entre les composants dans VueJS

Yosvel Quintero Arguelles
la source
1
Merci, donc je vais essayer de mutualiser mon code via des événements!
al3x
5
lorsque vous copiez / collez des éléments, il est agréable de mentionner également la source.
Mihai Vilcu
17
Ceci est utile dans la communication de l'enfant au parent. Mais y a-t-il une manière similaire de le faire du parent à l'enfant? Par exemple, avant d'autoriser l'utilisateur à ajouter un nouvel enfant, je veux que tous ceux existants soient validés - la logique de validation est en enfant, donc je veux les parcourir tous et exécuter par exemple la méthode validate ().
Mateusz Bartkowiak
66
Cela répond à la question opposée à ce qui a été réellement demandé. La réponse de Desmond Lua répond à la question réelle.
Bojan Markovic
4
Le bus événement est n ° 1 sur la liste des antipatterns de vue de Chris Fritz . Tout ce qui peut être modélisé avec des événements et un état distribué peut être modélisé avec un état global et une liaison bidirectionnelle, et en général, vous serez beaucoup mieux lotis.
bbsimonbb
1

Ref et bus d'événements ont tous deux des problèmes lorsque le rendu de votre contrôle est affecté par v-if. J'ai donc décidé d'opter pour une méthode plus simple.

L'idée est d'utiliser un tableau comme file d'attente pour envoyer des méthodes qui doivent être appelées au composant enfant. Une fois le composant monté, il traitera cette file d'attente. Il surveille la file d'attente pour exécuter de nouvelles méthodes.

(Empruntant du code à la réponse de Desmond Lua)

Code du composant parent:

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

Ceci est le code pour 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>

Et il y a beaucoup à faire pour s'améliorer comme passer processMethodsQueueà un mixin ...

mohghaderi
la source
0

Pour communiquer un composant enfant avec un autre composant enfant, j'ai créé une méthode dans le parent qui appelle une méthode dans un enfant avec:

this.$refs.childMethod()

Et d'un autre enfant que j'ai appelé la méthode racine:

this.$root.theRootMethod()

Cela a fonctionné pour moi.

Jonathan Arias
la source
Cette réponse manque beaucoup d'explications
vsync