Como acessar um método filho do pai em vue.js

97

Eu tenho dois componentes aninhados, qual é a maneira correta de acessar os métodos filhos do pai?

this.$children[0].myMethod() parece funcionar, mas é bem feio, não é, o que pode ser melhor maneira:

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

export default {
  components: {
   child
  },
  mounted () {
    this.$children[0].myMethod()
  }
}
</script>
al3x
fonte
Primeiro, pergunte a si mesmo se você realmente precisa. Se todo o estado da sua página estiver em uma loja, como deveria estar, não há necessidade de comunicação pai-filho.
bbsimonbb
7
@bbsimonbb O estado é diferente dos eventos. Trata-se especificamente de disparar eventos filho do pai. Você também pode fazer qualquer coisa para a qual usaria o Vuex passando um prop downstream, mas isso requer que o componente filho observe o prop / store quanto a alterações para que você emule efetivamente RPC com alterações de dados que são simplesmente erradas quando tudo o que você quer é acionar uma ação no componente.
Bojan Markovic

Respostas:

253

Você pode usar ref .

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

Se você não gosta de acoplamento forte, pode usar o Event Bus conforme mostrado por @Yosvel Quintero. Abaixo está outro exemplo de como usar o barramento de eventos passando no barramento como suporte.

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

Código do componente.

<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
fonte
39
Esta é a resposta correta, que realmente lê a pergunta real. A resposta selecionada na verdade responde à pergunta oposta (como acionar um método no pai a partir do componente filho).
Bojan Markovic
1
O link para Event Bus que esta resposta está vinculando, está redirecionando para State Management , após ler o comentário @bbsimonbb , meio que faz sentido.
Eido95 de
2
Vale a pena mencionar que se você usar this.$refs., não deve carregar componente filho dinamicamente.
1_bug de
obrigado senhor! Você me salvou de muitos problemas. Eu estava corrigindo um problema de produção e procurando respostas desesperadamente! <3
Osama Ibrahim
this.$ref.refparece retornar uma matriz. Então, para mim this.$refs.ref[0].autofocus();funcionou
Jozef Plata
26

Comunicação pai-filho no VueJS

Dado que uma instância raiz do Vue pode ser acessada por todos os descendentes via this.$root, um componente pai pode acessar os componentes filhos via this.$childrenarray e um componente filho pode acessar seu pai via this.$parent, seu primeiro instinto pode ser acessar esses componentes diretamente.

A documentação do VueJS alerta contra isso especificamente por duas razões muito boas:

  • Ele acopla fortemente o pai ao filho (e vice-versa)
  • Você não pode confiar no estado do pai, visto que ele pode ser modificado por um componente filho.

A solução é usar a interface de eventos personalizados do Vue

A interface de eventos implementada pelo Vue permite que você se comunique para cima e para baixo na árvore de componentes. Aproveitando a interface de evento personalizado, você tem acesso a quatro métodos:

  1. $on() - permite que você declare um ouvinte em sua instância Vue com o qual ouvir eventos
  2. $emit() - permite que você acione eventos na mesma instância (self)

Exemplo usando $on()e $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>

Resposta retirada da postagem original: Comunicação entre componentes no VueJS

Yosvel Quintero Arguelles
fonte
1
Obrigado, então vou tentar mutualizar meu código via eventos!
al3x
5
ao copiar / colar coisas, é bom mencionar também a fonte.
Mihai Vilcu
17
Isso é útil na comunicação da criança para o pai. Mas existe uma maneira semelhante de fazer isso de pai para filho? Por exemplo, antes de permitir que o usuário adicione um novo filho, quero que todos os existentes sejam validados - a lógica de validação está no filho, então quero passar por todos eles e executar, por exemplo, o método validate ().
Mateusz Bartkowiak
68
Isso responde à pergunta oposta ao que foi realmente perguntado. A resposta de Desmond Lua responde à pergunta real.
Bojan Markovic
4
O ônibus de eventos é o nº 1 na lista de antipadrões vue de Chris Fritz . Qualquer coisa que possa ser modelada com eventos e estado distribuído pode ser modelada com estado global e vinculação bidirecional e, em geral, você ficará muito melhor.
bbsimonbb
1

O barramento de referência e de evento apresenta problemas quando a renderização do controle é afetada por v-if. Então, decidi ir com um método mais simples.

A ideia é usar um array como uma fila para enviar métodos que precisam ser chamados para o componente filho. Assim que o componente for montado, ele processará essa fila. Ele observa a fila para executar novos métodos.

(Pegando emprestado algum código da resposta de Desmond Lua)

Código do componente pai:

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

Este é o código para 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>

E há muito espaço para melhorias, como mudar processMethodsQueuepara um mixin ...

Mohghaderi
fonte
0

Para comunicar um componente filho com outro componente filho, criei um método no pai que chama um método em um filho com:

this.$refs.childMethod()

E da outra criança chamei o método raiz:

this.$root.theRootMethod()

Funcionou para mim

Jonathan Arias
fonte
Esta resposta carece de muitas explicações
vsync