Як отримати доступ до дочірнього методу від батьків у vue.js


91

У мене є два вкладені компоненти, який правильний спосіб отримати доступ до дочірніх методів від батьків?

this.$children[0].myMethod() здається, робить фокус, але це досить потворно, чи не так, що може бути кращим способом:

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

export default {
  components: {
   child
  },
  mounted () {
    this.$children[0].myMethod()
  }
}
</script>

По-перше, запитайте себе, чи справді вам це потрібно. Якщо весь стан вашої сторінки знаходиться в магазині, як і повинно бути, немає необхідності у спілкуванні батьків і дітей.
bbsimonbb

7
@bbsimonbb Держава відрізняється від подій. Це конкретно про ініціювання дочірніх подій від батьків. Ви також можете робити все, що б ви використовували Vuex, передаючи проп нижче, але для цього потрібно, щоб дочірній компонент спостерігав за зміною prop / store, щоб ви ефективно емулювали 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
Посилання на шину подій , на яке пов'язує ця відповідь, перенаправляє на управління державою , після прочитання коментаря @bbsimonbb , це має сенс.
Eido95,

2
Варто згадати, якщо ви використовуєте this.$refs., ви не повинні завантажувати дочірній компонент динамічно.
1_bug

спасибі пане! Ти врятував мені багато клопоту. Я виправляв виробничу проблему і відчайдушно шукав відповіді! <3
Усама Ібрагім

this.$ref.refздається, повертає масив. Тож для мене this.$refs.ref[0].autofocus();працювало
Йозеф Плата

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

5
при копіюванні / вставці матеріалів приємно також згадати джерело.
Mihai Vilcu

17
Це корисно у спілкуванні від дитини до батьків. Але чи існує подібний спосіб зробити це від батьків до дітей? Наприклад, перш ніж дозволити користувачеві додати нову дочірню організацію, я хочу, щоб усі існуючі були перевірені - логіка перевірки є дочірньою, тому я хочу пройти їх усі і виконати, наприклад, метод validate ().
Матеуш Бартковяк,

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()

У мене це спрацювало.


Ці відповіді не мають багато пояснень
vsync
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.