Chame um método de componente Vue.js de fora do componente

229

Digamos que eu tenho uma instância principal do Vue que possui componentes filhos. Existe uma maneira de chamar um método pertencente a um desses componentes completamente fora da instância do Vue?

Aqui está um exemplo:

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>

Então, quando clico no botão interno, o increaseCount()método é vinculado ao seu evento de clique e é chamado. Não há como vincular o evento ao botão externo, cujo evento de clique eu estou ouvindo com o jQuery, portanto, precisarei de outra maneira de chamar increaseCount.

EDITAR

Parece que isso funciona:

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

No entanto, essa não é uma boa solução, pois estou referenciando o componente por seu índice na matriz filho e, com muitos componentes, é improvável que ele permaneça constante e o código seja menos legível.

harryg
fonte
1
Eu adicionei uma resposta usando mxins, se você quiser experimentá-lo. Na minha opinião, eu prefiro configurar o aplicativo dessa maneira.
Omar Tanti

Respostas:

275

No final, optei por usar a refdiretiva de Vue . Isso permite que um componente seja referenciado pelo pai para acesso direto.

Por exemplo

Tenha um componente registrado na minha instância pai:

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

Renderize o componente no template / html com uma referência:

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

Agora, em outro lugar, posso acessar o componente externamente

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

Veja este violino para um exemplo: https://jsfiddle.net/xmqgnbu3/1/

(exemplo antigo usando o Vue 1: https://jsfiddle.net/6v7y6msr/ )

harryg
fonte
6
Isso porque você provavelmente não o definiu. Olhe para o violino ligado.
harryg
29
Se você estiver usando o webpack, não poderá acessar vmdevido à forma como ele escopo os módulos. Você pode fazer algo como window.app = vmno seu main.js. Fonte: forum.vuejs.org/t/how-to-access-vue-from-chrome-console/3606
tw airball
1
Essa abordagem pode ser considerada normal? Ou isso é um hack?
CENT1PEDE
3
Existe uma definição tão oficial do que é um hack versus o código "normal", mas, em vez de chamar essa abordagem de hack (ou procurar uma maneira "menos hacky" de obter a mesma coisa), provavelmente é melhor questionar por que você precisa fazer isto. Em muitos casos, pode ser mais elegante usar o sistema de eventos do Vue para acionar o comportamento de componentes externos ou até mesmo perguntar por que você está acionando componentes externamente.
harryg
8
Isso pode surgir se você estiver tentando integrar um componente de exibição em uma página já existente. Em vez de redesenhar completamente a página, às vezes é bom adicionar funcionalidades adicionais.
Allen Wang
37

Desde o Vue2, isso se aplica:

var bus = new Vue()

// no método do componente A

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

// no gancho criado do componente B

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

  // ...
})

Veja aqui os documentos do Vue. E aqui estão mais detalhes sobre como configurar exatamente esse barramento de eventos.

Se você quiser obter mais informações sobre quando usar propriedades, eventos e / ou gerenciamento centralizado de estados, consulte este artigo .

musicformellons
fonte
1
Curto e grosso! Se você não gosta da busvariável global , pode dar um passo adiante e injetar o barramento no seu componente usando adereços . Sou relativamente novo no assunto, portanto não posso garantir que isso seja idiomático.
byxor
31

Você pode usar o sistema de eventos Vue

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

e

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

Aqui está o violino: http://jsfiddle.net/hfalucas/wc1gg5v4/59/

Helder Lucas
fonte
5
@GusDeCooL o exemplo foi editado. Não que, após o Vuejs 2.0, alguns métodos usados ​​tenham sido preteridos
Helder Lucas
1
Funciona bem se houver apenas 1 instância do componente, mas se houver muitos casos, funciona melhor aproveitamento $ refs.component.method ()
Koronos
22

Uma versão ligeiramente mais simples (mais simples) da resposta aceita:

Tenha um componente registrado na instância pai:

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

Renderize o componente no template / html com uma referência:

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

Acesse o método do componente:

<script>
    this.$refs.foo.doSomething();
</script>
Vencedor
fonte
22

Você pode definir ref para componentes filho e, em pai, pode chamar via $ refs:

Adicione ref ao componente filho:

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

Adicione evento de clique ao pai:

<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
fonte
2
a solução mais limpa até agora.
grreeenn
Ótima resposta. Vou apenas editar o html para ficar um pouco menor na vertical. Atualmente, só consigo ver 'Internal Button' quando o executo, o que foi confuso.
Jesse Reza Khorasanee
8

Digamos que você tenha um child_method()componente filho:

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

Agora você deseja executar o child_methodcomponente pai from:

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

Se você deseja executar um método do componente pai a partir do componente filho:

this.$parent.name_of_method()

NOTA: Não é recomendável acessar o componente filho e pai como este.

Em vez disso, como prática recomendada, use Adereços e Eventos para a comunicação entre pais e filhos.

Se você deseja comunicação entre componentes, use vuex ou barramento de eventos

Por favor, leia este artigo muito útil


roli roli
fonte
Sim, você pode, mas não é considerado "prática recomendada": para baixo para Propriedades de uso filho, para cima para eventos de uso Pai. Para cobrir também 'lateralmente', use eventos personalizados ou, por exemplo, vuex. Veja este bom artigo para mais informações.
Musicformellons
Sim, não é recomendado fazê-lo.
roli roli
3

Esta é uma maneira simples de acessar os métodos de um componente de outro componente

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

Aqui está um simples

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

Não tenho certeza se é o caminho certo, mas este funciona para mim.
Primeiro importe o componente que contém o método que você deseja chamar no seu componente

import myComponent from './MyComponent'

e chame qualquer método de MyCompenent

myComponent.methods.doSomething()
Rohit Kumar
fonte
isso não dá acesso a nenhum dado no seu componente. se você doSomethingestiver usando alguma coisa dos dados, esse método será inútil.
cyboashu
-7

Eu usei uma solução muito simples. Incluí um elemento HTML, que chama o método, no meu componente Vue que seleciono, usando o Vanilla JS, e aciono o clique!

No componente Vue, incluí algo como o seguinte:

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

Que eu uso usando o Vanilla JS:

const btnReload = document.querySelector('[data-id="btnReload"]');
btnReload.click();                
Gorbas
fonte
Isso não é considerado uma boa prática de maneira alguma, especialmente no contexto da pergunta do OP.
Devo da web híbrida