Como chamar a função no componente filho nos eventos pai

164

Contexto

No Vue 2.0, a documentação e outros indicam claramente que a comunicação de pai para filho ocorre por meio de adereços.

Questão

Como um pai ou mãe diz a seu filho que um evento aconteceu por meio de acessórios?

Devo apenas assistir a um objeto chamado evento? Isso não parece certo, nem alternativas ( $emit/ $oné da criança para a mãe, e um modelo de hub é para elementos distantes).

Exemplo

Eu tenho um contêiner pai e ele precisa informar ao contêiner filho que não há problema em envolver determinadas ações em uma API. Eu preciso ser capaz de disparar funções.

jbodily
fonte

Respostas:

234

Dê ao componente filho ae refuse-o $refspara chamar um método diretamente no componente filho.

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

Para mais informações, consulte a documentação do Vue em refs .

joerick
fonte
13
Dessa maneira, os componentes pai e filho são acoplados. Para eventos reais, dizer quando você não pode simplesmente mudar um suporte para desencadear uma ação, eu iria com a solução de ônibus sugerido por @Roy J
Jared
3
uma referência aos documentos seria útil também vuejs.org/v2/guide/…
ctf0
No componente filho, por razões especiais, tive que usar v-once para encerrar a reatividade. Portanto, passar o suporte de pai para filho não era uma opção, então essa solução fez o truque!
John
1
pergunta para iniciantes: Por que usar, em refvez de criar um suporte, que observe seu valor e depois o emita para outra função no pai? Quero dizer, ele tem muitas coisas para fazer, mas está usando refaté seguro? Obrigado
Irfandy Jip 21/01
5
@IrfandyJip - sim, refé seguro. Geralmente, é desencorajado porque a comunidade Vue prefere passar o estado para os filhos e os eventos de volta aos pais. De um modo geral, isso leva a componentes mais isolados e consistentes internamente (uma coisa boa ™). Mas, se as informações que você está passando para a criança realmente são um evento (ou um comando), modificar o estado não é o padrão certo. Nesse caso, chamar um método usando a refé totalmente bom e não vai falhar nem nada.
joerick
76

O que você está descrevendo é uma mudança de estado no pai. Você passa isso para a criança através de um suporte. Como você sugeriu, você faria watchisso. Quando a criança toma uma ação, ela notifica o pai por meio de um emite o pai pode alterar o estado novamente.

Se você realmente deseja transmitir eventos para uma criança, pode fazer isso criando um barramento (que é apenas uma instância do Vue) e passando-o para a criança como suporte .

Roy J
fonte
2
Penso que esta é a única resposta em conformidade com o guia de estilo e as melhores práticas oficiais do Vue.JS. Se você usar a abreviação v-modeldo componente, também poderá redefinir facilmente o valor emitindo o evento correspondente com menos código.
Falco
Por exemplo, quero dar um alerta quando um usuário clica em um botão. Você propor por exemplo: - assistir a um flag - definir este sinalizador de 0 a 1 quando ocorre um clique, - fazer alguma coisa - bandeira de reset
Sinan Erdem
9
É muito desconfortável, você precisa criar um extra propem uma criança, uma propriedade extra datae adicionar watch... Seria confortável se houvesse suporte interno para, de alguma forma, transferir eventos de pai para filho. Esta situação ocorre com bastante frequência.
Илья Зеленько
1
Como afirmado por @ ИльяЗеленько, isso acontece com bastante frequência, seria uma dádiva de Deus no momento.
Craig
1
Obrigado @RoyJ, acho que isso exige que o suporte de ônibus exista quando a criança assina, suponho que toda a idéia de enviar eventos para crianças seja desencorajada no Vue.
Ben Winding
35

Você pode usar $emite $on. Usando o código @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);
    }
  }
})

Exemplo de execução: https://jsfiddle.net/rjurado/m2spy60r/1/

drinor
fonte
6
Estou surpreso que funcione. Eu pensei que emitir para uma criança era um antipadrão, ou que a intenção era emitir apenas de criança para mãe. Há algum problema em potencial na direção contrária?
Jbodily 6/03
2
Isso pode não ser considerado o melhor caminho, eu não sei, mas se você sabe o que está fazendo, acho que não há problema. A outra maneira é usar o barramento central: vuejs.org/v2/guide/…
drinor
14
Isso cria um acoplamento entre a criança e os pais e é considerado má prática
morrislaptop
5
Isso funciona apenas porque o pai não é um componente, mas na verdade um aplicativo vue. Na realidade, isso está usando a instância vue como um barramento.
Julio Rodrigues
2
@Bsienn a chamada para isso. $ Parent torna esse componente dependente do pai. usa $ emit to e props para que as únicas dependências sejam através do sistema de comunicação do Vue. Essa abordagem permite que o mesmo componente seja usado em qualquer lugar da hierarquia de componentes.
morrislaptop
6

Se você tiver tempo, use o armazenamento Vuex para assistir variáveis ​​(também conhecido como estado) ou acionar (conhecido como despachar) uma ação diretamente.

brightknight08
fonte
2
devido à reatividade do vuejs / vuex, que é a melhor abordagem, no pai faça uma ação / mutação que altere um valor da propriedade vuex e no filho tenha um valor calculado que obtém o mesmo vuex $ store.state.property.value ou "watch "método que fazer algo quando vuex '' muda $ store.state.property.value
FabianSilva
6

Não gostou da abordagem de barramento de eventos usando $onligações no filho durante create. Por quê? As createchamadas subseqüentes (estou usando vue-router) vinculam o manipulador de mensagens mais de uma vez - levando a várias respostas por mensagem.

A solução ortodoxa de passar adereços de pai para filho e colocar um observador de propriedades na criança funcionou um pouco melhor. O único problema é que a criança só pode agir em uma transição de valor. Passar a mesma mensagem várias vezes precisa de algum tipo de contabilidade para forçar uma transição, para que a criança possa pegar a mudança.

Descobri que, se envolver a mensagem em uma matriz, ela sempre acionará o observador de crianças - mesmo que o valor permaneça o mesmo.

Pai:

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

Criança:

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

HTML:

<parent>
   <child v-bind="{ 'msgChild': msgChild }"></child>
</parent>
Jason Stewart
fonte
1
Eu acho que isso não funcionará se msgChild sempre tiver o mesmo status no pai. Por exemplo: eu quero um componente que abre um modal. O pai ou mãe não se importa se o status atual é aberto ou fechado, apenas deseja abrir o modal a qualquer momento. Portanto, se o pai fizer isso.msgChild = true; o modal está fechado, e então o pai não this.msgChild = true, a criança não vai receber o evento
Jorge Sainz
1
@JorgeSainz: É por isso que estou agrupando o valor em uma matriz antes de atribuí-lo ao item de dados. Sem agrupar o valor em uma matriz, ele se comporta exatamente como você especifica. Portanto, msgChild = true, msgChild = true - nenhum evento. msgChild = [verdadeiro], msgChild = [verdadeiro] - evento!
21318 Jason Stewart
1
Eu não vi. Obrigado pelo esclarecimento.
Jorge Sainz
Isso é legal, mas parece um pouco brincalhão. Vou usá-lo, já que é mais limpo do que usar o componente ref hack e menos complicado que a solução de barramento de eventos. Sei que o vue deseja desacoplar e permitir apenas alterações de estado para afetar o componente, mas deve haver alguma maneira interna de chamar os métodos de uma criança, se necessário. Talvez um modificador em um suporte que uma vez que mude de estado, você possa redefini-lo automaticamente para um valor padrão para que o inspetor esteja pronto para a próxima alteração de estado. De qualquer forma, obrigado por postar sua descoberta.
Craig
6

Uma maneira simples e dissociada de chamar métodos em componentes filhos é emitindo um manipulador do filho e depois invocando-o do pai.

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>

O pai controla as funções e chamadas do manipulador filho sempre que necessário.

nilobarp
fonte
Eu gosto de onde esta solução está indo, mas o que exatamente é "this.setter" no pai?
Craig
É a referência da função setValue emitida pelo componente filho como um argumento para o evento do manipulador.
Nilobarp 07/07/19
2

O exemplo abaixo é auto-explicativo. onde referências e eventos podem ser usados ​​para chamar a função de e para pai e filho.

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

Penso que devemos ter uma consideração sobre a necessidade dos pais de usar os métodos da criança. De fato, os pais não precisam se preocupar com o método da criança, mas podem tratar o componente filho como uma FSA (máquina de estados finitos). para controlar o estado do componente filho. Portanto, a solução para observar a mudança de status ou apenas usar a função de computação é suficiente

user10097040
fonte
2
Se você está dizendo "os pais nunca devem se preocupar em controlar os filhos", há casos em que isso é necessário. Considere um componente de contagem regressiva. Os pais podem querer reiniciar o cronômetro para recomeçar. Simplesmente usar adereços não é suficiente, pois passar do tempo = 60 para o tempo = 60 não modificará o suporte. O cronômetro deve expor uma função de "redefinição" que os pais possam chamar conforme apropriado.
tbm
0

você pode usar a tecla para recarregar o componente filho usando a tecla

<component :is="child1" :filter="filter" :key="componentKey"></component>

Se você deseja recarregar o componente com o novo filtro, se clicar no botão filtrar o componente filho

reloadData() {            
   this.filter = ['filter1','filter2']
   this.componentKey += 1;  
},

e use o filtro para ativar a função

Ardian Zack
fonte