Jak uzyskać dostęp do metody potomnej od rodzica w vue.js


91

Mam dwa zagnieżdżone komponenty, jaki jest właściwy sposób dostępu do metod potomnych od rodzica?

this.$children[0].myMethod() wydaje się działać, ale jest dość brzydki, prawda, co może być lepszego:

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

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

Najpierw zadaj sobie pytanie, czy naprawdę musisz. Jeśli cały stan strony znajduje się w sklepie, tak jak powinien, nie ma potrzeby komunikacji rodzic-dziecko.
bbsimonbb

7
@bbsimonbb Stan różni się od stanu wydarzeń. Dotyczy to w szczególności wyzwalania zdarzeń podrzędnych od rodzica. Możesz także zrobić wszystko, do czego używałbyś Vuex, przekazując rekwizyt pod prąd, ale wymaga to, aby komponent potomny obserwował właściwość / magazyn pod kątem zmian, aby skutecznie emulować RPC ze zmianami danych, co jest po prostu błędne, gdy wszystko, czego chcesz, to wywołać akcję w komponencie.
Bojan Markovic

Odpowiedzi:


244

Możesz użyć 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 },
})

Jeśli nie lubisz ścisłego połączenia, możesz użyć Event Bus, jak pokazano na @Yosvel Quintero. Poniżej znajduje się kolejny przykład wykorzystania magistrali zdarzeń przez przekazanie jej jako rekwizytów.

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

Kod elementu.

<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
To jest poprawna odpowiedź, która faktycznie odczytuje rzeczywiste pytanie. Wybrana odpowiedź faktycznie odpowiada na przeciwne pytanie (jak wywołać metodę na rodzica z komponentu podrzędnego).
Bojan Markovic

1
Łącze do magistrali zdarzeń, które łączy ta odpowiedź, przekierowuje do zarządzania stanem , po przeczytaniu komentarza @bbsimonbb ma sens.
Eido95,

2
Warto wspomnieć, że jeśli używasz this.$refs., nie powinieneś dynamicznie ładować komponentu potomnego.
1_bug

Dziękuję Panu! Oszczędziłeś mi wielu kłopotów. Naprawiałem problem z produkcją i desperacko szukałem odpowiedzi! <3
Osama Ibrahim

26

Komunikacja rodzic-dziecko w VueJS

Biorąc pod uwagę, że instancja root Vue jest dostępna dla wszystkich potomków za pośrednictwem this.$root, komponent nadrzędny może uzyskać dostęp do komponentów potomnych za pośrednictwem this.$childrentablicy, a komponent potomny może uzyskać dostęp do swojego rodzica za pośrednictwem this.$parent, twoim pierwszym odruchem może być bezpośredni dostęp do tych komponentów.

Dokumentacja VueJS ostrzega przed tym konkretnie z dwóch bardzo dobrych powodów:

  • Ściśle łączy rodzica z dzieckiem (i odwrotnie)
  • Nie możesz polegać na stanie rodzica, biorąc pod uwagę, że może on być modyfikowany przez składnik potomny.

Rozwiązaniem jest użycie niestandardowego interfejsu zdarzeń Vue

Interfejs zdarzeń zaimplementowany przez Vue umożliwia komunikację w górę iw dół drzewa komponentów. Wykorzystanie interfejsu zdarzeń niestandardowych daje dostęp do czterech metod:

  1. $on() - pozwala zadeklarować odbiornik w Twojej instancji Vue, za pomocą którego można słuchać zdarzeń
  2. $emit() - umożliwia wyzwalanie zdarzeń na tej samej instancji (własnej)

Przykład użycia $on()i $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>

Odpowiedź zaczerpnięta z oryginalnego postu: Komunikacja między komponentami w VueJS


1
Dziękuję, więc spróbuję zmutualizować mój kod poprzez wydarzenia!
al3x

5
podczas kopiowania / wklejania dobrze jest również wspomnieć o źródle.
Mihai Vilcu

17
Jest to pomocne w komunikacji między dzieckiem a rodzicem. Ale czy istnieje podobny sposób na zrobienie tego od rodzica do dziecka? Np. Zanim pozwolę użytkownikowi na dodanie nowego dziecka, chcę, aby wszystkie istniejące zostały sprawdzone - logika walidacji jest w child, więc chcę przejść przez nie wszystkie i wykonać np. Metodę validate ().
Mateusz Bartkowiak

65
To odpowiada na pytanie przeciwne do tego, co zostało faktycznie zadane. Odpowiedź Desmonda Lui odpowiada na rzeczywiste pytanie.
Bojan Markovic

4
Magistrala eventowa jest numerem 1 na liście antywzorów Chrisa Fritza . Wszystko, co można modelować za pomocą zdarzeń i stanu rozproszonego, można modelować za pomocą stanu globalnego i dwukierunkowego wiązania, a ogólnie rzecz biorąc, będzie znacznie lepiej.
bbsimonbb

1

Zarówno magistrala odwołań, jak i magistrala zdarzeń mają problemy, gdy wpływa na renderowanie sterowania v-if. Więc zdecydowałem się na prostszą metodę.

Pomysł polega na użyciu tablicy jako kolejki do wysyłania metod, które muszą zostać wywołane do komponentu potomnego. Po zamontowaniu komponent będzie przetwarzał tę kolejkę. Obserwuje kolejkę do wykonania nowych metod.

(Pożyczenie kodu z odpowiedzi Desmonda Lua)

Kod komponentu nadrzędnego:

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

To jest kod dla 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>

Jest dużo miejsca na ulepszenia, na przykład przejście processMethodsQueuedo miksu ...


0

Aby komunikować komponent potomny z innym komponentem potomnym, stworzyłem metodę w rodzicu, która wywołuje metodę w dziecku za pomocą:

this.$refs.childMethod()

A od innego dziecka, które nazwałem metodą root:

this.$root.theRootMethod()

U mnie to zadziałało.


Ta odpowiedź nie ma wielu wyjaśnień
vsync
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.