Appeler une méthode de composant Vue.js depuis l'extérieur du composant

229

Disons que j'ai une instance principale de Vue qui a des composants enfants. Existe-t-il un moyen d'appeler une méthode appartenant à l'un de ces composants depuis l'extérieur de l'instance de Vue?

Voici un exemple:

var vm = new Vue({
  el: '#app',
  components: {
    'my-component': { 
      template: '#my-template',
      data: function() {
        return {
          count: 1,
        };
      },
      methods: {
        increaseCount: function() {
          this.count++;
        }
      }
    },
  }
});

$('#external-button').click(function()
{
  vm['my-component'].increaseCount(); // This doesn't work
});
<script src="http://vuejs.org/js/vue.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="app">
  
  <my-component></my-component>
  <br>
  <button id="external-button">External Button</button>
</div>
  
<template id="my-template">
  <div style="border: 1px solid; padding: 5px;">
  <p>A counter: {{ count }}</p>
  <button @click="increaseCount">Internal Button</button>
    </div>
</template>

Ainsi, lorsque je clique sur le bouton interne, la increaseCount()méthode est liée à son événement click, elle est donc appelée. Il n'y a aucun moyen de lier l'événement au bouton externe, dont j'écoute l'événement click avec jQuery, j'ai donc besoin d'une autre façon d'appeler increaseCount.

ÉDITER

Il semble que cela fonctionne:

vm.$children[0].increaseCount();

Cependant, ce n'est pas une bonne solution car je fais référence au composant par son index dans le tableau des enfants, et avec de nombreux composants, il est peu probable qu'il reste constant et le code est moins lisible.

Harry
la source
1
J'ai ajouté une réponse en utilisant mxins si vous voulez l'essayer. À mon avis, je préfère configurer l'application de cette façon.
Omar Tanti

Réponses:

276

Au final, j'ai opté pour l'utilisation de la refdirective Vue . Cela permet à un composant d'être référencé à partir du parent pour un accès direct.

Par exemple

Faire enregistrer un composant sur mon instance parent:

var vm = new Vue({
    el: '#app',
    components: { 'my-component': myComponent }
});

Rendez le composant dans template / html avec une référence:

<my-component ref="foo"></my-component>

Maintenant, ailleurs, je peux accéder au composant en externe

<script>
vm.$refs.foo.doSomething(); //assuming my component has a doSomething() method
</script>

Voir ce violon pour un exemple: https://jsfiddle.net/xmqgnbu3/1/

(ancien exemple utilisant Vue 1: https://jsfiddle.net/6v7y6msr/ )

Harry
la source
6
C'est parce que vous ne l'avez probablement pas défini. Regardez le violon lié.
Harry
29
Si vous utilisez webpack, vous ne pourrez pas y accéder en vmraison de la façon dont il étend les modules. Vous pouvez faire quelque chose comme window.app = vmdans votre main.js. Source: forum.vuejs.org/t/how-to-access-vue-from-chrome-console/3606
tw airball
1
Cette approche peut-elle être considérée comme normale? Ou est-ce un hack?
CENT1PEDE
3
Il y a une définition si officielle de ce qui est un hack vs ce qui est un codage "normal", mais plutôt que d'appeler cette approche un hack (ou chercher un moyen "moins hacky" de réaliser la même chose), il est probablement préférable de se demander pourquoi vous devez faire ce. Dans de nombreux cas, il peut être plus élégant d'utiliser le système d'événements de Vue pour déclencher le comportement d'un composant externe, ou même de demander pourquoi vous déclenchez des composants en externe.
harryg
8
Cela peut apparaître si vous essayez d'intégrer un composant de vue dans une page déjà existante. Plutôt que de repenser entièrement la page, il est parfois utile d'ajouter progressivement des fonctionnalités supplémentaires.
Allen Wang
38

Depuis Vue2, cela s'applique:

var bus = new Vue()

// dans la méthode du composant A

bus.$emit('id-selected', 1)

// dans le crochet créé du composant B

bus.$on('id-selected', function (id) {

  // ...
})

Voir ici pour les documents Vue. Et voici plus de détails sur la façon de configurer exactement ce bus d'événements.

Si vous souhaitez plus d'informations sur l'utilisation des propriétés, des événements et / ou de la gestion centralisée des états, consultez cet article .

musicformellons
la source
1
Court et doux! Si vous n'aimez pas la busvariable globale , vous pouvez aller plus loin et injecter le bus dans votre composant à l'aide d' accessoires . Je suis relativement nouveau en vue, donc je ne peux pas vous assurer que c'est idiomatique.
byxor
31

Vous pouvez utiliser le système d'événement Vue

vm.$broadcast('event-name', args)

et

 vm.$on('event-name', function())

Voici le violon: http://jsfiddle.net/hfalucas/wc1gg5v4/59/

Helder Lucas
la source
5
@GusDeCooL l'exemple a été modifié. Non pas qu'après Vuejs 2.0, certaines méthodes utilisées aient été dépréciées
Helder Lucas
1
Fonctionne bien s'il n'y a qu'une seule instance de composant, mais s'il y en a plusieurs, il vaut mieux utiliser $ refs.component.method ()
Koronos
22

Une version légèrement différente (plus simple) de la réponse acceptée:

Avoir un composant enregistré sur l'instance parent:

export default {
    components: { 'my-component': myComponent }
}

Rendez le composant dans template / html avec une référence:

<my-component ref="foo"></my-component>

Accédez à la méthode des composants:

<script>
    this.$refs.foo.doSomething();
</script>
Victor
la source
22

Vous pouvez définir la référence pour les composants enfants, puis dans le parent, vous pouvez appeler via $ refs:

Ajouter une référence au composant enfant:

<my-component ref="childref"></my-component>

Ajouter un événement de clic au parent:

<button id="external-button" @click="$refs.childref.increaseCount()">External Button</button>

var vm = new Vue({
  el: '#app',
  components: {
    'my-component': { 
      template: '#my-template',
      data: function() {
        return {
          count: 1,
        };
      },
      methods: {
        increaseCount: function() {
          this.count++;
        }
      }
    },
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  
  <my-component ref="childref"></my-component>
  <button id="external-button" @click="$refs.childref.increaseCount()">External Button</button>
</div>
  
<template id="my-template">
  <div style="border: 1px solid; padding: 2px;" ref="childref">
    <p>A counter: {{ count }}</p>
    <button @click="increaseCount">Internal Button</button>
  </div>
</template>

Cong Nguyen
la source
2
la solution la plus propre à ce jour.
grreeenn
Très bonne réponse. Je vais juste éditer le html pour qu'il soit un peu plus petit verticalement. Actuellement, je ne vois que le bouton interne lorsque je l'exécute, ce qui peut être déroutant.
Jesse Reza Khorasanee
9

Supposons que vous ayez un child_method()dans le composant enfant:

export default {
    methods: {
        child_method () {
            console.log('I got clicked')
        }
    }
}

Vous voulez maintenant exécuter le child_methodcomposant parent parent:

<template>
    <div>
        <button @click="exec">Execute child component</button>
        <child-cmp ref="child"></child_cmp> <!-- note the ref="child" here -->
    </div>
</template>

export default {
    methods: {
        exec () { //accessing the child component instance through $refs
            this.$refs.child.child_method() //execute the method belongs to the child component
        }
    }
}

Si vous souhaitez exécuter une méthode de composant parent à partir du composant enfant:

this.$parent.name_of_method()

REMARQUE: il n'est pas recommandé d'accéder au composant enfant et parent comme ceci.

Au lieu de cela, comme meilleure pratique, utilisez les accessoires et les événements pour la communication parent-enfant.

Si vous voulez une communication entre les composants, utilisez sûrement vuex ou bus d'événements

Veuillez lire cet article très utile


roli roli
la source
Oui, vous pouvez, mais ce n'est pas considéré comme une «meilleure pratique»: vers le bas pour les propriétés d'utilisation des enfants, vers le haut pour les événements d'utilisation des parents. Pour couvrir également «latéralement», utilisez des événements personnalisés, ou par exemple vuex. Voir ce bel article pour plus d'informations.
musicformellons
Oui, il n'est pas recommandé de le faire.
roli roli
4

Il s'agit d'un moyen simple d'accéder aux méthodes d'un composant à partir d'un autre composant

// This is external shared (reusable) component, so you can call its methods from other components

export default {
   name: 'SharedBase',
   methods: {
      fetchLocalData: function(module, page){
          // .....fetches some data
          return { jsonData }
      }
   }
}

// This is your component where you can call SharedBased component's method(s)
import SharedBase from '[your path to component]';
var sections = [];

export default {
   name: 'History',
   created: function(){
       this.sections = SharedBase.methods['fetchLocalData']('intro', 'history');
   }
}
Dotnetgang
la source
2

En voici un simple

this.$children[indexOfComponent].childsMethodName();
Pratik Khadtale
la source
1

Je ne sais pas si c'est la bonne façon, mais celle-ci fonctionne pour moi.
Importez d'abord le composant qui contient la méthode que vous souhaitez appeler dans votre composant

import myComponent from './MyComponent'

puis appelez n'importe quelle méthode de MyCompenent

myComponent.methods.doSomething()
Rohit Kumar
la source
cela ne vous donne accès à aucune donnée de votre composant. si vous doSomethingutilisez quelque chose de données, cette méthode est inutile.
cyboashu
-6

J'ai utilisé une solution très simple. J'ai inclus un élément HTML, qui appelle la méthode, dans mon composant Vue que je sélectionne, en utilisant Vanilla JS, et je déclenche un clic!

Dans le composant Vue, j'ai inclus quelque chose comme ceci:

<span data-id="btnReload" @click="fetchTaskList()"><i class="fa fa-refresh"></i></span>

Que j'utilise avec Vanilla JS:

const btnReload = document.querySelector('[data-id="btnReload"]');
btnReload.click();                
Gorbas
la source
Ceci n'est pas du tout considéré comme une bonne pratique, en particulier dans le contexte de la question d'OP.
Développement web hybride