Aufrufen der Funktion für untergeordnete Komponenten bei übergeordneten Ereignissen

164

Kontext

In Vue 2.0 zeigen die Dokumentation und andere deutlich, dass die Kommunikation von Eltern zu Kind über Requisiten erfolgt.

Frage

Wie sagt ein Elternteil seinem Kind über Requisiten, dass ein Ereignis passiert ist?

Soll ich mir nur eine Requisite namens Event ansehen? Das fühlt sich nicht richtig an und es gibt auch keine Alternativen ( $emit/ $onist für Kinder zu Eltern und ein Hub-Modell ist für entfernte Elemente).

Beispiel

Ich habe einen übergeordneten Container und er muss seinem untergeordneten Container mitteilen, dass es in Ordnung ist, bestimmte Aktionen auf einer API auszuführen. Ich muss in der Lage sein, Funktionen auszulösen.

jbodily
quelle

Antworten:

233

Geben Sie der untergeordneten Komponente ein refund $refsrufen Sie mit dieser Methode direkt eine Methode für die untergeordnete Komponente auf.

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

Weitere Informationen finden Sie in der Vue-Dokumentation zu Refs .

Joerick
quelle
13
Auf diese Weise werden übergeordnete und untergeordnete Komponenten gekoppelt. Für reale Ereignisse, sagen wir, wenn Sie nicht einfach eine Requisite ändern können, um eine Aktion auszulösen, würde ich mit der Buslösung gehen, die von @Roy J
Jared
3
Ein Verweis auf die Dokumente wäre auch hilfreich. vuejs.org/v2/guide/…
ctf0
In meiner untergeordneten Komponente musste ich aus besonderen Gründen v-einmal verwenden, um die Reaktivität zu beenden. Daher war es keine Option, die Requisite vom Elternteil an das Kind weiterzugeben, also hat diese Lösung den Trick getan!
John
1
Anfängerfrage: Warum eine Requisite verwenden, refanstatt sie zu erstellen, die ihren Wert überwacht und sie dann an eine andere Funktion im übergeordneten Element ausgibt? Ich meine, es hat eine Menge zu tun, aber ist die Verwendung refsogar sicher? Vielen Dank
Irfandy Jip
5
@ IrfandyJip - ja, refist sicher. Im Allgemeinen wird davon abgeraten, da die Vue-Community den Status lieber an Kinder und Ereignisse an die Eltern zurückgibt. Im Allgemeinen führt dies zu isolierteren, intern konsistenten Komponenten (eine gute Sache ™). Wenn es sich bei den Informationen, die Sie an das Kind weitergeben, jedoch tatsächlich um ein Ereignis (oder einen Befehl) handelt, ist das Ändern des Status nicht das richtige Muster. In diesem Fall ist das Aufrufen einer Methode mit a refvöllig in Ordnung und es wird nicht abstürzen oder so.
Joerick
76

Was Sie beschreiben, ist eine Zustandsänderung im übergeordneten Element. Sie geben das über eine Requisite an das Kind weiter. Wie Sie vorgeschlagen haben, würden Sie watchdiese Requisite. Wenn das Kind Maßnahmen ergreift, benachrichtigt es das Elternteil über ein emit, und das Elternteil kann dann den Status erneut ändern.

Wenn Sie wirklich Ereignisse an ein Kind weitergeben möchten, können Sie dies tun, indem Sie indem Sie einen Bus erstellen (der nur eine Vue-Instanz ist) und ihn als Requisite an das Kind übergeben .

Roy J.
quelle
2
Ich denke, dies ist die einzige Antwort im Einklang mit dem offiziellen Vue.JS-Styleguide und den Best Practices. Wenn Sie die Kurzform v-modelfür die Komponente verwenden, können Sie den Wert auch einfach zurücksetzen, indem Sie das entsprechende Ereignis mit weniger Code ausgeben.
Falco
Zum Beispiel möchte ich eine Warnung geben, wenn ein Benutzer auf eine Schaltfläche klickt. Schlagen Sie zum Beispiel vor: - Beobachten Sie eine Flagge - setzen Sie diese Flagge von 0 auf 1, wenn ein Klick auftritt, - tun Sie etwas - setzen Sie die Flagge zurück
Sinan Erdem
9
Es ist sehr unangenehm, Sie müssen ein Extra propin einem Kind erstellen , eine Extra-Eigenschaft in data, und dann hinzufügenwatch ... Es wäre bequem, wenn es eine integrierte Unterstützung gäbe, um Ereignisse irgendwie von einem Elternteil auf ein Kind zu übertragen. Diese Situation tritt ziemlich oft auf.
Злья Зеленько
1
Wie von @ ИльяЗеленько angegeben, kommt es ziemlich oft vor, es wäre im Moment ein Glücksfall.
Craig
1
Vielen Dank an @RoyJ. Ich denke, dass die Bus-Requisite vorhanden sein muss, wenn das Kind sie abonniert. Ich nehme jedoch an, dass die ganze Idee, Ereignisse an Kinder zu senden, in Vue entmutigt ist.
Ben Winding
35

Sie können $emitund verwenden $on. Verwenden des @ RoyJ-Codes:

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

Laufendes Beispiel: https://jsfiddle.net/rjurado/m2spy60r/1/

Drinor
quelle
6
Ich bin überrascht, dass es funktioniert. Ich dachte, die Emission an ein Kind sei ein Anti-Muster, oder die Absicht sei, dass die Emission nur vom Kind zum Elternteil erfolgt. Gibt es mögliche Probleme, in die andere Richtung zu gehen?
Jbodily
2
Dies kann nicht als der beste Weg angesehen werden, ich weiß es nicht, aber wenn Sie wissen, was Sie tun, ist es kein Problem. Der andere Weg ist die Verwendung des zentralen Busses: vuejs.org/v2/guide/…
Drinor
14
Dies schafft eine Kopplung zwischen dem Kind und dem Elternteil und wird als schlechte Praxis angesehen
morrislaptop
5
Dies funktioniert nur, weil das übergeordnete Element keine Komponente, sondern eine Vue-App ist. In Wirklichkeit wird die vue-Instanz als Bus verwendet.
Julio Rodrigues
2
@Bsienn der Aufruf von this. $ Parent macht diese Komponente vom übergeordneten Element abhängig. verwendet $ emit to und Requisiten, sodass die einzigen Abhängigkeiten über das Kommunikationssystem von Vue bestehen. Mit diesem Ansatz kann dieselbe Komponente an einer beliebigen Stelle in der Komponentenhierarchie verwendet werden.
Morrislaptop
6

Wenn Sie Zeit haben, verwenden Sie den Vuex-Speicher, um Variablen (auch bekannt als Status) zu beobachten oder eine Aktion direkt auszulösen (auch bekannt als Versand).

brightknight08
quelle
2
Aufgrund der Reaktivität von vuejs / vuex, die der beste Ansatz ist, führen Sie im übergeordneten Element eine Aktion / Mutation durch, die einen vuex-Eigenschaftswert ändert, und haben im untergeordneten Element einen berechneten Wert, der denselben vuex $ store.state.property.value oder eine "watch" erhält "Methode, die etwas tut, wenn sich vuex" $ store.state.property.value "ändert
FabianSilva
6

Mochte den Event-Bus-Ansatz mit $onBindungen im Kind während nicht create. Warum? Nachfolgende createAnrufe (ich benutzevue-router ) binden den Nachrichtenhandler mehr als einmal, was zu mehreren Antworten pro Nachricht führt.

Die orthodoxe Lösung, Requisiten von einem Elternteil an ein Kind weiterzugeben und dem Kind einen Immobilienbeobachter zu geben, funktionierte etwas besser. Das einzige Problem ist, dass das Kind nur auf einen Werteübergang reagieren kann. Wenn Sie dieselbe Nachricht mehrmals weitergeben, ist eine Art Buchhaltung erforderlich, um einen Übergang zu erzwingen, damit das Kind die Änderung abholen kann.

Ich habe festgestellt, dass die Nachricht, wenn ich sie in ein Array einbinde, immer den untergeordneten Watcher auslöst - auch wenn der Wert gleich bleibt.

Elternteil:

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

Kind:

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

HTML:

<parent>
   <child v-bind="{ 'msgChild': msgChild }"></child>
</parent>
Jason Stewart
quelle
1
Ich denke, das wird nicht funktionieren, wenn msgChild immer den gleichen Status auf dem Elternteil hat. Zum Beispiel: Ich möchte eine Komponente, die ein Modal öffnet. Dem Elternteil ist es egal, ob der aktuelle Status geöffnet oder geschlossen ist, er möchte nur jederzeit das Modal öffnen. Wenn das übergeordnete Element dies tut.msgChild = true; Das Modal ist geschlossen, und dann macht der Elternteil dies. msgChild = true, das Kind erhält das Ereignis nicht
Jorge Sainz
1
@JorgeSainz: Deshalb verpacke ich den Wert in ein Array, bevor ich ihn dem Datenelement zuweise. Ohne den Wert in ein Array zu verpacken, verhält es sich genau so, wie Sie es angegeben haben. Also, msgChild = true, msgChild = true - kein Ereignis. msgChild = [true], msgChild = [true] - Ereignis!
Jason Stewart
1
Ich habe es nicht gesehen. Vielen Dank für die Klarstellung
Jorge Sainz
Das ist cool, fühlt sich aber etwas hackisch an. Ich werde es verwenden, da es sauberer ist als die Verwendung des Komponenten-Ref-Hacks und weniger kompliziert als die Event-Bus-Lösung. Ich weiß, dass vue eine Entkopplung wünscht und nur Statusänderungen zulässt, um die Komponente zu beeinflussen, aber es sollte eine integrierte Möglichkeit geben, die Methoden eines Kindes bei Bedarf aufzurufen. Möglicherweise ein Modifikator für eine Requisite, der nach dem Ändern des Status automatisch auf einen Standardwert zurückgesetzt werden kann, damit der Beobachter für die nächste Statusänderung bereit ist. Trotzdem danke, dass du deinen Fund gepostet hast.
Craig
6

Eine einfache entkoppelte Methode zum Aufrufen von Methoden für untergeordnete Komponenten besteht darin, einen Handler vom untergeordneten Element auszugeben und ihn dann vom übergeordneten Element aufzurufen.

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>

Der Elternteil verfolgt die Funktionen des untergeordneten Handlers und ruft bei Bedarf auf.

Nilobarp
quelle
Ich mag, wohin diese Lösung führt, aber was genau ist "this.setter" im übergeordneten Element?
Craig
Dies ist die setValue-Funktionsreferenz, die von der untergeordneten Komponente als Argument für das Handler-Ereignis ausgegeben wird.
Nilobarp
2

Das folgende Beispiel ist selbsterklärend. Hier können Referenzen und Ereignisse verwendet werden, um Funktionen von und zu Eltern und Kind aufzurufen.

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

Ich denke, wir sollten über die Notwendigkeit nachdenken, dass Eltern die Methoden des Kindes anwenden müssen. Tatsächlich müssen Eltern die Methode des Kindes nicht betreffen, sondern können die untergeordnete Komponente als FSA (Finite State Machine) .Parents-Komponente behandeln So reicht die Lösung aus, um die Statusänderung zu beobachten oder einfach die Rechenfunktion zu verwenden

user10097040
quelle
2
Wenn Sie sagen "Eltern sollten sich niemals mit der Kontrolle der Kinder befassen", gibt es Fälle, in denen dies notwendig ist. Betrachten Sie eine Countdown-Timer-Komponente. Der Elternteil möchte möglicherweise den Timer zurücksetzen, um von vorne zu beginnen. Es reicht nicht aus, nur Requisiten zu verwenden, da ein Wechsel von Zeit = 60 zu Zeit = 60 die Requisite nicht verändert. Der Timer sollte eine 'Reset'-Funktion bereitstellen, die der Elternteil entsprechend aufrufen kann.
tbm
0

Sie können den Schlüssel verwenden, um die untergeordnete Komponente mithilfe des Schlüssels neu zu laden

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

Wenn Sie eine Komponente mit neuem Filter neu laden möchten, klicken Sie auf die untergeordnete Komponente, wenn Sie auf die Schaltfläche klicken

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

und verwenden Sie den Filter, um die Funktion auszulösen

Ardian Zack
quelle