Как получить доступ к дочернему методу от родителя в vue.js

91

У меня есть два вложенных компонента, как правильно получить доступ к дочерним методам от родителя?

this.$children[0].myMethod() кажется, помогает, но это довольно уродливо, не правда ли, что может быть лучше:

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

export default {
  components: {
   child
  },
  mounted () {
    this.$children[0].myMethod()
  }
}
</script>
al3x
источник
Во-первых, спросите себя, действительно ли вам это нужно. Если все состояние вашей страницы находится в магазине, как и должно быть, нет необходимости в общении между родителями и детьми.
bbsimonbb
7
@bbsimonbb Состояние отличается от событий. Речь идет конкретно о запуске дочерних событий от родителя. Вы также можете делать все, что бы вы использовали для Vuex, передавая опору вниз по потоку, но для этого требуется, чтобы дочерний компонент следил за опорой / хранилищем на предмет изменений, чтобы вы эффективно эмулировали RPC с изменениями данных, что просто неправильно, когда все, что вы хотите, это запускает действие в компоненте.
Боян Маркович

Ответы:

244

Вы можете использовать исх .

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

Если вам не нравится тесная связь, вы можете использовать шину событий, как показано @Yosvel Quintero. Ниже приведен еще один пример использования шины событий путем передачи шины в качестве опоры.

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

Код компонента.

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

Десмонд Луа
источник
38
Это правильный ответ, который на самом деле читал сам вопрос. Выбранный ответ фактически отвечает на противоположный вопрос (как запустить метод родительского компонента из дочернего компонента).
Боян Маркович
1
Ссылка на шину событий , на которую ссылается этот ответ, перенаправляется в State Management , после прочтения комментария @bbsimonbb это имеет смысл.
Eido95
2
Стоит упомянуть, что если вы используете this.$refs., вы не должны загружать дочерний компонент динамически.
1_bug
Спасибо, сэр! Вы избавили меня от многих проблем. Я исправлял производственную проблему и отчаянно искал ответы! <3
Усама Ибрагим
27

Связь родитель-ребенок в VueJS

Если корневой экземпляр Vue доступен для всех потомков через this.$root, родительский компонент может получить доступ к дочерним компонентам через this.$childrenмассив, а дочерний компонент может получить доступ к своему родительскому через this.$parent, ваш первый инстинкт может заключаться в прямом доступе к этим компонентам.

Документация VueJS предупреждает об этом по двум очень веским причинам:

  • Он прочно связывает родителя с ребенком (и наоборот)
  • Вы не можете полагаться на состояние родителя, поскольку оно может быть изменено дочерним компонентом.

Решение - использовать пользовательский интерфейс событий Vue.

Интерфейс событий, реализованный Vue, позволяет вам взаимодействовать вверх и вниз по дереву компонентов. Использование интерфейса настраиваемых событий дает вам доступ к четырем методам:

  1. $on() - позволяет вам объявить слушателя в вашем экземпляре Vue, с помощью которого вы будете слушать события
  2. $emit() - позволяет запускать события на одном и том же экземпляре (самостоятельно)

Пример использования $on()и $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>

Ответ взят из исходного сообщения: общение между компонентами в VueJS

Йосвел Кинтеро Аргуэльес
источник
1
Спасибо, я попробую объединить свой код с помощью событий!
al3x 04
5
при копировании / вставке материала неплохо также указать источник.
Михай Вилку
17
Это помогает в общении от ребенка к родителю. Но есть ли аналогичный способ передачи от родителя к ребенку? Например, прежде чем я разрешаю пользователю добавлять нового дочернего элемента, я хочу, чтобы все существующие были проверены - логика проверки находится в дочернем элементе, поэтому я хочу пройти через все из них и выполнить, например, метод validate ().
Mateusz
66
Это отвечает на вопрос, противоположный тому, что было задано на самом деле. Ответ Десмонда Луа отвечает на актуальный вопрос.
Боян Маркович
4
Шина событий занимает первое место в списке антипаттернов Криса Фрица . Все, что можно смоделировать с помощью событий и распределенного состояния, можно смоделировать с помощью глобального состояния и двусторонней привязки, и в целом вам будет намного лучше.
bbsimonbb
1

Ref и шина событий имеют проблемы, когда на рендеринг элемента управления влияет v-if . Итак, я решил пойти по более простому методу.

Идея состоит в использовании массива в качестве очереди для отправки методов, которые необходимо вызвать дочернему компоненту. Как только компонент будет смонтирован, он обработает эту очередь. Он наблюдает за очередью, чтобы выполнить новые методы.

(Заимствование кода из ответа Десмонда Луа)

Код родительского компонента:

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

Это код для 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>

И есть много возможностей для улучшения, например, переход processMethodsQueueна миксин ...

Могхадери
источник
0

Чтобы связать дочерний компонент с другим дочерним компонентом, я создал метод в родительском элементе, который вызывает метод в дочернем элементе с помощью:

this.$refs.childMethod()

А от другого ребенка я вызвал корневой метод:

this.$root.theRootMethod()

У меня это сработало.

Джонатан Ариас
источник
В этом
ответе