web-dev-qa-db-fra.com

Comment appeler une fonction sur un composant enfant lors d'événements parent

Le contexte

Dans Vue 2.0, la documentation et autres indiquent clairement que la communication d'un parent à l'autre s'effectue via des accessoires.

Question

Comment un parent peut-il informer son enfant qu'un événement s'est produit via des accessoires?

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

Exemple

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

100
jbodily

Donnez au composant enfant un ref et utilisez $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 de Vue sur les références .

133
joerick

Ce que vous décrivez est un changement d'état dans le parent. Vous transmettez cela à l'enfant via un accessoire. Comme vous l'avez suggéré, vous auriez watch cet accessoire. Lorsque l'enfant prend des mesures, il en informe le parent via un emit. Ce dernier peut alors modifier à 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éant un bus (qui n'est qu'une instance de Vue) et en le transmettant à l'enfant en tant qu'accessoire .

55
Roy J

Vous pouvez utiliser $emit et $on. Utilisation du code @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 en cours d'exécution: https://jsfiddle.net/rjurado/m2spy60r/1/

26
drinor

Si vous avez le temps, utilisez le magasin Vuex pour regarder les variables (état) ou déclenchez (action) directement une action.

6
brightknight08

N'aimait pas l'approche approche par bus d'événements utilisant $on les liaisons dans l'enfant pendant create. Pourquoi? Les appels ultérieurs create (j'utilise vue-router) lient le gestionnaire de messages plusieurs fois, ce qui entraîne plusieurs réponses par message.

La solution orthodoxe consistant à transmettre les accessoires d'un parent à l'autre et à placer un observateur de propriété dans l'enfant fonctionnait un peu mieux . Seul problème étant que l'enfant ne peut agir que sur une transition de valeur. Passer le même message plusieurs fois nécessite une sorte de comptabilité pour forcer une transition afin que l'enfant puisse prendre en compte le changement.

J'ai constaté que si j'emballe le message dans un tableau, il déclenchera toujours l'observateur enfant, même si la valeur reste la même.

Parent:

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

Enfant:

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

HTML:

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

Un moyen simple et découplé d'appeler des méthodes sur des composants enfants consiste à émettre un gestionnaire à partir de l'enfant, puis à l'invoquer à partir du parent.

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>

Le parent assure le suivi des fonctions et des appels du gestionnaire enfant si nécessaire.

3
nilobarp

L'exemple ci-dessous est explicite. Où les références et les événements peuvent être utilisés pour appeler une fonction 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>
2
Mukundhan

Je pense que nous devrions prendre en compte la nécessité pour le parent d'utiliser les méthodes de l'enfant.En fait, les parents ne doivent pas s'intéresser à la méthode de l'enfant, mais peuvent traiter le composant enfant comme un composant FSA (machine à états finis) .Parents donc la solution pour regarder le changement de statut ou simplement utiliser la fonction de calcul suffit

1
user10097040

Vous pouvez utiliser un mixin pour définir un attribut de données partagé. Changez-le dans le parent, regardez-le chez l'enfant:

// mixin
export default {
  data() {
    return  {
      clicked: false
    }
  }
}

// parent
export default {
  mixins: [myMixin],
  methods: {
    btnClick() {
      this.clicked = true
    }
  }
}

// child
export default {
  mixins: [myMixin],
  watch: {
    clicked(val) {
      if(val) {
        // yay
      }
    }
  }
}
0
digout