Як викликати функцію дочірнього компонента на батьківських подіях


164

Контекст

У Vue 2.0 документація та інші чітко вказують, що спілкування від батька до дитини відбувається через реквізит.

Питання

Як батько повідомляє своїй дитині, що відбулася подія за допомогою реквізиту?

Потрібно просто спостерігати за подією, яку називають опорою? Це не вірно, ані альтернативи ( $emit/ $onпризначено для дитини-батька, а модель концентратора - для віддалених елементів).

Приклад

У мене є батьківський контейнер, і він повинен сказати своєму дочірньому контейнерові, що це добре, щоб здійснити певні дії в API. Мені потрібно вміти запускати функції.

Відповіді:


235

Дайте дочірньому компоненту a refі використовуйте $refsдля виклику методу безпосередньо на дочірньому компоненті.

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

Докладніші відомості див. У документації Vue на відповідні документи .


13
Таким чином батьківські та дочірні компоненти поєднуються. Що стосується реальних подій, скажімо, коли ви не можете просто змінити опору, щоб викликати дію, я б пішов із рішенням автобуса, запропонованим @Roy J
Джаред


У моєму дочірньому компоненті з особливих причин мені довелося використовувати v-один раз для припинення реактивності. Таким чином, передача опори від батька до дитини не була можливою, тому це рішення зробило трюк!
Іван

1
питання для новачків: Навіщо використовувати refзамість створення опори, яка спостерігає за її значенням, а потім передає її іншій функції в батьківському? Я маю на увазі, що це має зробити багато справ, але це refнавіть безпечно? Спасибі
Ірфанді Джип

5
@IrfandyJip - так, refце безпечно. Як правило, це не відлякує, оскільки громада Vue вважає за краще передавати стан дітям, а події повертатись батькові. Взагалі, це призводить до більш ізольованих, внутрішньо-послідовних компонентів (гарна річ ™). Але якщо інформація, яку ви передаєте дитині, насправді є подією (або командою), зміна стану не є правильною схемою. У такому випадку виклик методу за допомогою a refзовсім непогано, і він не зірветься чи нічого.
joerick

76

Те, що ви описуєте, - це зміна стану батьків. Ви передаєте це дитині через опору. Як ви запропонували, ви б watchце підтримали. Коли дитина вживає заходів, вона сповіщає батьків за допомогою emit, і батько може потім змінити стан знову.

Якщо ви справді хочете передати події дитині, ви можете зробити це, створивши шину (яка є лише екземпляром Vue) та передавши її дитині як опору .


2
Я думаю, що це єдина відповідь відповідно до офіційного посібника зі стилів Vue.JS та найкращих практик. Якщо ви використовуєте скорочення v-modelкомпонента, ви також можете легко скинути значення, випромінюючи відповідну подію з меншим кодом.
Фалько

Наприклад, я хочу оповістити, коли користувач натискає кнопку. Чи пропонуєте ви, наприклад: - дивіться прапор - встановіть цей прапор від 0 до 1, коли відбувається натискання, - зробіть щось - скиньте прапор
Sinan Erdem

9
Це дуже незручно, ви повинні створити зайве propв дитині, додаткове майно в data, а потім додати watch... Було б зручно, якби була вбудована підтримка, щоб якось перенести події від батьків до дитини. Така ситуація трапляється досить часто.
Илья Зеленько

1
Як заявляє @ ІльяЗеленько, це трапляється досить часто, зараз це було б знахідкою.
Крейг

1
Дякую @RoyJ, я думаю, що потрібна підтримка шини, коли дитина підписується на неї, проте, я думаю, вся ідея надсилати події дітям відлякує у Vue.
Бен Віндінг

35

Ви можете використовувати $emitі $on. Використовуючи код @RoyJ:

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

Приклад роботи: https://jsfiddle.net/rjurado/m2spy60r/1/


6
Я здивований, що це працює. Я вважав, що випромінювання дитини є анти-закономірністю, або що наміри полягають у тому, щоб випромінювання було лише від дитини до батька. Чи є якісь потенційні проблеми з іншим шляхом?
jbodily

2
Це, можливо, не вважається найкращим способом, я не знаю, але якщо ти знаєш, чим ти займаєшся, я річ закинула - це не проблема. Інший спосіб - скористатися центральним автобусом: vuejs.org/v2/guide/…
харчування

14
Це створює зв’язок між дитиною та батьком і вважається поганою практикою
morrislaptop

5
Це працює лише тому, що батько не є компонентом, а насправді vue-додатком. Насправді це використовується екземпляр vue як шина.
Хуліо Родрігес

2
@Bsienn викликає це. $ Parent робить цей компонент залежним від батьківського. використовує $ emit і реквізити, тому єдині залежності знаходяться через систему зв'язку Vue. Цей підхід дозволяє використовувати один і той же компонент в будь-якій частині ієрархії компонентів.
morrislaptop

6

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


2
завдяки реактивності vuejs / vuex, що є найкращим підходом, у батьків роблять дію / мутацію, що змінюють значення властивості vuex, а у дитини мають обчислене значення, яке отримує цей самий vuex $ store.state.property.value або "watch "метод, який щось робить, коли vuex" $ store.state.property.value "змінюється
FabianSilva

6

Не сподобався подієво-автобусний підхід використанням $onприв’язок у дитини протягом create. Чому? Подальші createдзвінки (я використовую vue-router) пов'язують обробник повідомлень не раз - це призводить до отримання декількох відповідей на повідомлення.

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

Я виявив, що якщо я загортаю повідомлення в масив, воно завжди запускатиме дитину-спостерігача - навіть якщо значення залишається таким же.

Батько:

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

Дитина:

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

HTML:

<parent>
   <child v-bind="{ 'msgChild': msgChild }"></child>
</parent>

1
Я думаю, це не спрацює, якщо msgChild завжди має однаковий статус у батьків. Наприклад: Я хочу компонент, який відкриває модальний. Батькові не байдуже, чи поточний статус відкритий чи закритий, він просто хоче відкрити модал будь-який момент. Отже, якщо батько робить це.msgChild = true; модаль закритий, і тоді батько робить це.msgChild = вірно, дитина не отримає події
Хорхе Сайнц

1
@JorgeSainz: Ось чому я загортаю значення в масив перед тим, як призначити його елементу даних. Без упаковки значення в масив він поводиться так само, як ви вказали. Отже, msgChild = true, msgChild = true - немає події. msgChild = [true], msgChild = [true] - подія!
Джейсон Стюарт

1
Я цього не бачив. Дякую за роз’яснення
Хорхе Сайнц

Це круто, але відчуває трохи хакі. Я збираюся використовувати його, оскільки він чистіший, ніж використання компонента ref hack і менш складний, ніж рішення шини подій. Я знаю, що vue хоче роз'єднати і лише дозволяти змінам стану впливати на компонент, але повинен бути якийсь вбудований спосіб викликати методи дитини, якщо це потрібно. Можливо, модифікатор опори, що після зміни статусу ви зможете автоматично скинути його до значення за замовчуванням, щоб спостерігач був готовий до наступної зміни стану. У будь-якому випадку дякую за публікацію своєї знахідки.
Крейг

6

Простий нерозв'язний спосіб викликати методи дочірніх компонентів - це випустити обробник від дитини і потім викликати його від батьків.

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/vue@2.5.17/dist/vue.js"></script>

<div id="app">
  <my-component @handler="setValueHandler"></my-component>
  <button @click="click">Click</button>  
</div>

Батько відслідковує функції довірителя та дзвонить, коли це необхідно.


Мені подобається, куди йде це рішення, але що саме є "this.setter" у батьків?
Крейг

Це посилання функції setValue, яке випромінює дочірній компонент як аргумент події обробника.
nilobarp

2

Наведений нижче приклад пояснює себе. де виклики та події можуть використовуватися для виклику функції від батьків та дитини.

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

1

Я думаю, що ми повинні мати роздуми про необхідність батьків використовувати методи дитини. Насправді, батькам не потрібно стосуватися методу дитини, але вони можуть ставитися до дочірнього компонента як до FSA (скінченного апарату). Батьковий компонент для контролю стану дочірнього компонента. Таким чином, достатньо рішення спостерігати за зміною статусу або просто використовувати функцію обчислення


2
Якщо ви говорите: "батьки ніколи не повинні займатися контролем над дітьми", є випадки, коли це необхідно. Розглянемо компонент таймера зворотного відліку. Батько, можливо, захоче скинути таймер для початку. Просте використання реквізиту недостатньо, оскільки перехід від часу = 60 до часу = 60 не змінює опору. Таймер повинен відкрити функцію "скидання", яку батько може викликати у відповідних випадках.
tbm

0

ви можете використовувати ключ для перезавантаження дочірнього компонента за допомогою ключа

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

Якщо ви хочете перезавантажити компонент новим фільтром, якщо натиснути кнопку фільтрувати дочірній компонент

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

і використовуйте фільтр для запуску функції

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