Wywołaj metodę komponentu Vue.js spoza komponentu

229

Załóżmy, że mam główną instancję Vue, która zawiera elementy potomne. Czy istnieje sposób wywołania metody należącej do jednego z tych komponentów spoza instancji Vue?

Oto przykład:

var vm = new Vue({
  el: '#app',
  components: {
    'my-component': { 
      template: '#my-template',
      data: function() {
        return {
          count: 1,
        };
      },
      methods: {
        increaseCount: function() {
          this.count++;
        }
      }
    },
  }
});

$('#external-button').click(function()
{
  vm['my-component'].increaseCount(); // This doesn't work
});
<script src="http://vuejs.org/js/vue.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="app">
  
  <my-component></my-component>
  <br>
  <button id="external-button">External Button</button>
</div>
  
<template id="my-template">
  <div style="border: 1px solid; padding: 5px;">
  <p>A counter: {{ count }}</p>
  <button @click="increaseCount">Internal Button</button>
    </div>
</template>

Kiedy klikam przycisk wewnętrzny, increaseCount()metoda jest powiązana ze zdarzeniem kliknięcia, więc zostaje wywołana. Nie ma możliwości powiązania zdarzenia z zewnętrznym przyciskiem, którego zdarzenia kliknięcia oczekuję za pomocą jQuery, więc będę potrzebować innego sposobu wywołania increaseCount.

EDYTOWAĆ

Wygląda na to, że działa:

vm.$children[0].increaseCount();

Nie jest to jednak dobre rozwiązanie, ponieważ odwołuję się do komponentu według jego indeksu w tablicy potomnej, a przy wielu komponentach jest mało prawdopodobne, aby pozostał on stały, a kod jest mniej czytelny.

harryg
źródło
1
Dodałem odpowiedź za pomocą mxins, jeśli chcesz spróbować. Moim zdaniem wolę tak skonfigurować aplikację.
Omar Tanti

Odpowiedzi:

275

W końcu zdecydowałem się na stosowanie dyrektywy Vueref . Umożliwia to odwołanie do komponentu od rodzica w celu uzyskania bezpośredniego dostępu.

Na przykład

Poproś o zarejestrowanie komponentu w mojej instancji nadrzędnej:

var vm = new Vue({
    el: '#app',
    components: { 'my-component': myComponent }
});

Renderuj komponent w szablonie / HTML z referencją:

<my-component ref="foo"></my-component>

Teraz gdzie indziej mogę uzyskać dostęp do komponentu na zewnątrz

<script>
vm.$refs.foo.doSomething(); //assuming my component has a doSomething() method
</script>

Zobacz to skrzypce na przykład: https://jsfiddle.net/xmqgnbu3/1/

(stary przykład z wykorzystaniem Vue 1: https://jsfiddle.net/6v7y6msr/ )

harryg
źródło
6
To dlatego, że prawdopodobnie go nie zdefiniowałeś. Spójrz na połączone skrzypce.
harryg
29
Jeśli używasz pakietu webpack, nie będziesz mieć dostępu vmdo niego ze względu na sposób, w jaki obejmuje on moduły. Możesz zrobić coś takiego window.app = vmw swoim main.js. Źródło: forum.vuejs.org/t/how-to-access-vue-from-chrome-console/3606
tw airball
1
Czy to podejście można uznać za normalne? Czy to hack?
CENT1PEDE
3
Istnieje tak oficjalna definicja tego, co jest hackiem, a czym jest „normalne” kodowanie, ale zamiast nazwać to podejście hackem (lub poszukać „mniej hackerskiego” sposobu osiągnięcia tego samego), prawdopodobnie lepiej jest zapytać, dlaczego należy to zrobić to. W wielu przypadkach bardziej eleganckie może być użycie systemu zdarzeń Vue do wyzwalania zachowania komponentów zewnętrznych, a nawet pytanie, dlaczego w ogóle wywołujesz komponenty.
harryg
8
Może się to pojawić, jeśli próbujesz zintegrować komponent widoku z już istniejącą stroną. Zamiast całkowicie przeprojektowywać stronę, czasem miło jest stopniowo dodawać dodatkową funkcjonalność.
Allen Wang
37

Od wersji Vue2 dotyczy to:

var bus = new Vue()

// w metodzie komponentu A.

bus.$emit('id-selected', 1)

// w utworzonym haku komponentu B.

bus.$on('id-selected', function (id) {

  // ...
})

Zobacz tutaj dokumenty Vue. A oto więcej szczegółów na temat tego, jak dokładnie skonfigurować tę magistralę zdarzeń.

Jeśli chcesz uzyskać więcej informacji o tym, kiedy korzystać z właściwości, zdarzeń i / lub scentralizowanego zarządzania stanem, przeczytaj ten artykuł .

musicformellons
źródło
1
Krótkie i słodkie! Jeśli nie podoba ci się buszmienna globalna , możesz pójść o krok dalej i wstrzyknąć magistralę do swojego komponentu za pomocą rekwizytów . Jestem stosunkowo nowy, aby vue, więc nie mogę cię zapewnić, że to idiomatyczne.
byxor
31

Możesz użyć systemu zdarzeń Vue

vm.$broadcast('event-name', args)

i

 vm.$on('event-name', function())

Oto skrzypce: http://jsfiddle.net/hfalucas/wc1gg5v4/59/

Helder Lucas
źródło
5
@GusDeCooL przykład został edytowany. Nie, że po Vuejs 2.0 niektóre stosowane tam metody zostały przestarzałe
Helder Lucas
1
Działa dobrze, jeśli jest tylko 1 instancja składnika, ale jeśli jest wiele instancji, lepiej użyć $ refs.component.method ()
Koronos
22

Nieco inna (prostsza) wersja zaakceptowanej odpowiedzi:

Poproś o zarejestrowanie komponentu w instancji nadrzędnej:

export default {
    components: { 'my-component': myComponent }
}

Renderuj komponent w szablonie / HTML z referencją:

<my-component ref="foo"></my-component>

Uzyskaj dostęp do metody komponentu:

<script>
    this.$refs.foo.doSomething();
</script>
Zwycięzca
źródło
22

Możesz ustawić ref dla komponentów potomnych, a następnie w rodzicu możesz wywoływać za pomocą $ refs:

Dodaj odwołanie do komponentu potomnego:

<my-component ref="childref"></my-component>

Dodaj zdarzenie kliknięcia do elementu nadrzędnego:

<button id="external-button" @click="$refs.childref.increaseCount()">External Button</button>

var vm = new Vue({
  el: '#app',
  components: {
    'my-component': { 
      template: '#my-template',
      data: function() {
        return {
          count: 1,
        };
      },
      methods: {
        increaseCount: function() {
          this.count++;
        }
      }
    },
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  
  <my-component ref="childref"></my-component>
  <button id="external-button" @click="$refs.childref.increaseCount()">External Button</button>
</div>
  
<template id="my-template">
  <div style="border: 1px solid; padding: 2px;" ref="childref">
    <p>A counter: {{ count }}</p>
    <button @click="increaseCount">Internal Button</button>
  </div>
</template>

Cong Nguyen
źródło
2
najczystsze jak dotąd rozwiązanie.
grreeenn
Świetna odpowiedź. Zamierzam edytować HTML, aby był nieco mniejszy w pionie. Obecnie widzę „wewnętrzny przycisk” tylko po uruchomieniu, co może być mylące.
Jesse Reza Khorasanee
8

Załóżmy, że masz element child_method()podrzędny:

export default {
    methods: {
        child_method () {
            console.log('I got clicked')
        }
    }
}

Teraz chcesz uruchomić child_methodkomponent nadrzędny:

<template>
    <div>
        <button @click="exec">Execute child component</button>
        <child-cmp ref="child"></child_cmp> <!-- note the ref="child" here -->
    </div>
</template>

export default {
    methods: {
        exec () { //accessing the child component instance through $refs
            this.$refs.child.child_method() //execute the method belongs to the child component
        }
    }
}

Jeśli chcesz wykonać metodę komponentu nadrzędnego z komponentu podrzędnego:

this.$parent.name_of_method()

UWAGA: Nie jest zalecane uzyskiwanie dostępu do elementu podrzędnego i nadrzędnego w ten sposób.

Zamiast tego, jako najlepszą praktykę, używaj Rekwizytów i wydarzeń do komunikacji rodzic-dziecko.

Jeśli chcesz komunikować się między komponentami, z pewnością użyj vuex lub magistrali zdarzeń

Przeczytaj ten bardzo pomocny artykuł


roli roli
źródło
Tak, możesz, ale nie uznaje się za „najlepszą praktykę”: w dół do właściwości użycia Dziecko, w górę do zdarzeń użycia Rodzic. Aby objąć również „boczne”, użyj niestandardowych zdarzeń lub np. Vuex. Zobacz ten miły artykuł, aby uzyskać więcej informacji.
musicformellons,
Tak, nie jest to zalecane.
roli roli
3

Jest to prosty sposób na uzyskanie dostępu do metod komponentu z innego komponentu

// This is external shared (reusable) component, so you can call its methods from other components

export default {
   name: 'SharedBase',
   methods: {
      fetchLocalData: function(module, page){
          // .....fetches some data
          return { jsonData }
      }
   }
}

// This is your component where you can call SharedBased component's method(s)
import SharedBase from '[your path to component]';
var sections = [];

export default {
   name: 'History',
   created: function(){
       this.sections = SharedBase.methods['fetchLocalData']('intro', 'history');
   }
}
Dotnetgang
źródło
1

Oto prosty

this.$children[indexOfComponent].childsMethodName();
Pratik Khadtale
źródło
1

Nie jestem pewien, czy to właściwa droga, ale ta działa dla mnie.
Najpierw zaimportuj komponent zawierający metodę, którą chcesz wywołać w komponencie

import myComponent from './MyComponent'

a następnie wywołać dowolną metodę MyCompenent

myComponent.methods.doSomething()
Rohit Kumar
źródło
nie daje to dostępu do żadnych danych w komponencie. jeśli doSomethingużywasz czegoś z danych, ta metoda jest bezużyteczna.
cyboashu
-7

Użyłem bardzo prostego rozwiązania. W wybranym przeze mnie komponencie Vue umieściłem element HTML, który wywołuje tę metodę, używając Vanilla JS i uruchamiam kliknięcie!

W komponencie Vue zamieściłem coś takiego:

<span data-id="btnReload" @click="fetchTaskList()"><i class="fa fa-refresh"></i></span>

Korzystam z Vanilla JS:

const btnReload = document.querySelector('[data-id="btnReload"]');
btnReload.click();                
Gorbas
źródło
W ogóle nie jest to uważane za dobrą praktykę, szczególnie w kontekście pytania PO.
Hybrid web dev