Вызовите метод компонента Vue.js снаружи компонента.

229

Допустим, у меня есть основной экземпляр Vue, имеющий дочерние компоненты. Есть ли способ полностью вызвать метод, принадлежащий одному из этих компонентов, вне экземпляра Vue?

Вот пример:

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>

Поэтому, когда я нажимаю внутреннюю кнопку, increaseCount()метод привязывается к событию click, поэтому он вызывается. Нет способа привязать событие к внешней кнопке, чье событие щелчка я слушаю с помощью jQuery, поэтому мне понадобится другой способ вызова increaseCount.

РЕДАКТИРОВАТЬ

Кажется, это работает:

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

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

harryg
источник
1
Я добавил ответ, используя mxins, если вы хотите попробовать. На мой взгляд, я предпочитаю настроить приложение таким образом.
Омар Танти

Ответы:

276

В конце концов я выбрал директиву Vueref . Это позволяет ссылаться на компонент от родителя для прямого доступа.

Например

Зарегистрировать компонент на моем родительском экземпляре:

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

Визуализируйте компонент в template / html со ссылкой:

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

Теперь в другом месте я могу получить доступ к компоненту извне

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

Посмотрите эту скрипку для примера: https://jsfiddle.net/xmqgnbu3/1/

(старый пример с использованием Vue 1: https://jsfiddle.net/6v7y6msr/ )

harryg
источник
6
Это потому, что вы, вероятно, не определили это. Посмотрите на связанную скрипку.
Гарриг
29
Если вы используете веб-пакет, то вы не сможете получить к нему доступ vmиз-за того, как он охватывает модули. Вы можете сделать что-то, как window.app = vmу вас main.js. Источник: forum.vuejs.org/t/how-to-access-vue-from-chrome-console/3606
два аэробуса
1
Можно ли считать такой подход нормальным? Или это взлом?
CENT1PEDE
3
Существует такое официальное определение того, что такое взлом против «нормального» кодирования, но вместо того, чтобы называть этот подход взломом (или искать «менее хакерский» способ достижения того же самого), вероятно, лучше задать вопрос, зачем вам это нужно этот. Во многих случаях может быть более элегантно использовать систему событий Vue для запуска поведения внешних компонентов или даже спросить, почему вы вообще запускаете компоненты извне.
Гарриг
8
Это может произойти, если вы пытаетесь интегрировать компонент представления в уже существующую страницу. Вместо того, чтобы полностью переделывать страницу, иногда полезно добавить дополнительные функции.
Аллен Ван
38

С Vue2 это относится:

var bus = new Vue()

// в методе компонента A

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

// в созданном хуке компонента B

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

  // ...
})

Смотрите здесь для Vue документы. А вот более подробно о том, как точно настроить эту шину событий.

Если вам нужна дополнительная информация о том, когда использовать свойства, события и / или централизованное управление состоянием, см. Эту статью .

musicformellons
источник
1
Коротко и сладко! Если вам не нравится глобальная busпеременная, вы можете пойти дальше и внедрить шину в свой компонент, используя реквизиты . Я относительно новичок в Vue, поэтому я не могу заверить вас, что это идиоматично.
byxor
31

Вы можете использовать систему событий Vue

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

и

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

Вот скрипка: http://jsfiddle.net/hfalucas/wc1gg5v4/59/

Хелдер Лукас
источник
5
@GusDeCooL пример был отредактирован. Не то чтобы после Vuejs 2.0 некоторые используемые там методы устарели
Хелдер Лукас
1
Работает хорошо, если есть только 1 экземпляр компонента, но если есть много экземпляров, лучше работает, используя $ refs.component.method ()
Koronos
22

Немного другая (более простая) версия принятого ответа:

Зарегистрируйте компонент в родительском экземпляре:

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

Визуализируйте компонент в template / html со ссылкой:

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

Доступ к методу компонента:

<script>
    this.$refs.foo.doSomething();
</script>
Виктор
источник
22

Вы можете установить ref для дочерних компонентов, тогда в parent можно вызывать через $ refs:

Добавьте ссылку на дочерний компонент:

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

Добавить событие клика к родителю:

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

Конг Нгуен
источник
2
самое чистое решение на данный момент.
Гривенн
Отличный ответ. Я просто собираюсь редактировать HTML, чтобы быть немного меньше по вертикали. В настоящее время я вижу только «Внутреннюю кнопку», когда запускаю ее, что может сбить с толку.
Джесси Реза Хорасани
9

Скажем, у вас есть child_method()дочерний компонент:

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

Теперь вы хотите выполнить child_methodиз родительского компонента:

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

Если вы хотите выполнить метод родительского компонента из дочернего компонента:

this.$parent.name_of_method()

ПРИМЕЧАНИЕ. Не рекомендуется обращаться к дочернему и родительскому компоненту, как это.

Вместо этого, в качестве лучшей практики, используйте Props & Events для общения между родителями и детьми.

Если вам нужна связь между компонентами, обязательно используйте vuex или шину событий

Пожалуйста, прочитайте эту очень полезную статью


Роли Роли
источник
Да, вы можете, но не считаться «наилучшей практикой»: вниз к свойствам использования дочерних элементов, вверх к событиям использования родительских функций. Чтобы также охватить «в сторону», используйте пользовательские события или, например, vuex. Смотрите эту хорошую статью для получения дополнительной информации.
musicformellons
Да, это не рекомендуется делать.
Роли Роли
4

Это простой способ доступа к методам компонента из другого компонента.

// 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
источник
2

Вот простой

this.$children[indexOfComponent].childsMethodName();
Пратик Хадтале
источник
1

Я не уверен, что это правильный путь, но этот работает для меня.
Сначала импортируйте компонент, который содержит метод, который вы хотите вызвать в вашем компоненте.

import myComponent from './MyComponent'

а затем вызвать любой метод MyCompenent

myComponent.methods.doSomething()
Рохит Кумар
источник
это не дает вам доступа к каким-либо данным в вашем компоненте. если вы doSomethingиспользуете какую-либо вещь из данных, этот метод бесполезен.
cyboashu
-6

Я использовал очень простое решение. Я включил элемент HTML, который вызывает метод, в мой компонент Vue, который я выбираю, используя Vanilla JS, и я запускаю щелчок!

В компонент Vue я включил что-то вроде следующего:

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

Что я использую, используя Vanilla JS:

const btnReload = document.querySelector('[data-id="btnReload"]');
btnReload.click();                
Gorbas
источник
Это не считается хорошей практикой вообще, особенно в контексте вопроса ФП.
Гибридный веб-разработчик