306 votes

Comment appeler la fonction d'un composant enfant sur les événements du parent ?

Contexte

Dans Vue 2.0, la documentation et autres indiquent clairement que la communication entre le parent et l'enfant se fait par le biais d'accessoires.

Question

Comment un parent indique-t-il à son enfant qu'un événement s'est produit via des props ?

Devrais-je simplement regarder un accessoire appelé événement ? Cela ne me semble pas correct, pas plus que les alternatives ( $emit / $on est pour l'enfant au parent, et un modèle de hub est pour les éléments distants).

Exemple

J'ai un conteneur parent et il doit dire à son conteneur enfant qu'il peut effectuer certaines actions sur une API. Je dois être capable de déclencher des fonctions.

423voto

joerick Points 6215

Donnez au composant enfant un ref et utiliser $refs pour appeler directement une méthode sur le composant enfant.

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

Pour plus d'informations, voir Documentation Vue sur les refs .

28 votes

De cette façon, les composants parent et enfant sont couplés. Pour les événements réels, par exemple lorsque vous ne pouvez pas simplement changer une prop pour déclencher une action, j'opterais pour la solution du bus suggérée par @Roy J.

4 votes

Une référence à la documentation serait également utile. vuejs.org/v2/guide/

0 votes

Dans mon composant enfant, pour des raisons particulières, j'ai dû utiliser v-once pour mettre fin à la réactivité. Ainsi, transmettre l'accessoire du parent à l'enfant n'était pas une option, et cette solution a fait l'affaire !

114voto

Roy J Points 423

Ce que vous décrivez est un changement d'état du parent. Vous le transmettez à l'enfant via un prop. Comme vous l'avez suggéré, vous devriez watch cet accessoire. Lorsque l'enfant entreprend une action, il en informe le parent par le biais d'un message de type emit et le parent peut alors changer à nouveau l'état.

var Child = {
  template: '<div>{{counter}}</div>',
  props: ['canI'],
  data: function () {
    return {
      counter: 0
    };
  },
  watch: {
    canI: function () {
      if (this.canI) {
        ++this.counter;
        this.$emit('increment');
      }
    }
  }
}
new Vue({
  el: '#app',
  components: {
    'my-component': Child
  },
  data: {
    childState: false
  },
  methods: {
    permitChild: function () {
      this.childState = true;
    },
    lockChild: function () {
      this.childState = false;
    }
  }
})

<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.2.1/vue.js"></script>
<div id="app">
<my-component :can-I="childState" v-on:increment="lockChild"></my-component>
<button @click="permitChild">Go</button>
</div>

Si vous voulez vraiment transmettre des événements à un enfant, vous pouvez le faire en créer un bus (qui est juste une instance Vue) et le passer à l'enfant en tant que prop .

4 votes

Je pense que c'est la seule réponse conforme au guide de style officiel de Vue.JS et aux meilleures pratiques. Si vous utilisez l'abréviation v-model sur le composant, vous pouvez aussi facilement réinitialiser la valeur en émettant l'événement correspondant avec moins de code.

0 votes

Par exemple, je veux donner une alerte lorsqu'un utilisateur clique sur un bouton. Proposez-vous par exemple : - de surveiller un drapeau - de faire passer ce drapeau de 0 à 1 lorsqu'un clic se produit, - de faire quelque chose - de remettre le drapeau à zéro.

0 votes

Je ne m'attendrais pas à ce que l'enfant déclenche une alerte en cas de clic dans le parent, mais si c'est ce que vous voulez faire, je passerais un bus à l'enfant et ferais en sorte que le parent émette sur le bus.

48voto

drinor Points 730

Vous pouvez utiliser $emit et $on . En utilisant le code de @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);
    }
  }
})

Exemple concret : https://jsfiddle.net/rjurado/m2spy60r/1/

7 votes

Je suis surpris que ça marche. Je pensais que l'émission vers un enfant était un anti-modèle, ou que l'intention était que l'émission se fasse uniquement d'enfant à parent. Y a-t-il des problèmes potentiels en allant dans l'autre sens ?

2 votes

Cette méthode n'est peut-être pas considérée comme la meilleure, je ne sais pas, mais si vous savez ce que vous faites, je pense qu'il n'y a pas de problème. L'autre moyen est d'utiliser le bus central : vuejs.org/v2/guide/

24 votes

Cela crée un couplage entre l'enfant et le parent et est considéré comme une mauvaise pratique.

11voto

Mukundhan Points 837

L'exemple ci-dessous s'explique de lui-même. où les références et les événements peuvent être utilisés pour appeler des fonctions depuis et vers le parent et l'enfant.

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

7voto

brightknight08 Points 76

Si vous avez le temps, utilisez Vuex store pour surveiller les variables (aka state) ou déclencher (aka dispatch) une action directement.

2 votes

En raison de la réactivité de vuejs/vuex c'est la meilleure approche, dans le parent faire une action/mutation qui change la valeur d'une propriété vuex et dans l'enfant avoir une valeur calculée qui obtient cette même vuex $store.state.property.value ou une méthode "watch" qui fait quelque chose quand vuex "$store.state.property.value" change.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X