vuejs оновити батьківські дані з дочірнього компонента


155

Я починаю грати з vuejs (2.0). Я побудував просту сторінку з одним компонентом на ній. На сторінці є один екземпляр Vue з даними. На цій сторінці я зареєстрував та додав компонент до html. Компонент має один input[type=text]. Я хочу, щоб це значення відображалося на батьківському (основний екземпляр Vue).

Як правильно оновити батьківські дані компонента? Передавання прив’язаного опори від батьків не добре, і він кидає певні попередження на консоль. Вони мають щось у своєму документі, але це не працює.


1
Чи можете ви додати спробували код, який не працює.
Саурах

Відповіді:


181

Двостороння прив'язка була припинена у Vue 2.0 на користь використання більш керованої подіями архітектури. Взагалі дитина не повинна мутувати свої реквізити. Швидше, це повинні $emitподії і нехай батько реагує на ці події.

У вашому конкретному випадку ви можете використовувати спеціальний компонент із v-model. Це особливий синтаксис, який дозволяє щось близьке до двостороннього прив'язки, але насправді є стенограмою для описаної вище архітектури, керованої подіями. Про це можна прочитати тут -> https://vuejs.org/v2/guide/components.html#Form-Input-Components-using-Custom-Events .

Ось простий приклад:

Vue.component('child', {
  template: '#child',
  
  //The child has a prop named 'value'. v-model will automatically bind to this prop
  props: ['value'],
  methods: {
    updateValue: function (value) {
      this.$emit('input', value);
    }
  }
});

new Vue({
  el: '#app',
  data: {
    parentValue: 'hello'
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>

<div id="app">
  <p>Parent value: {{parentValue}}</p>
  <child v-model="parentValue"></child>
</div>

<template id="child">
   <input type="text" v-bind:value="value" v-on:input="updateValue($event.target.value)">
</template>


Документи стверджують, що

<custom-input v-bind:value="something" v-on:input="something = arguments[0]"></custom-input>

еквівалентно

<custom-input v-model="something"></custom-input>

Ось чому опору на дитину потрібно назвати значенням, і чому дитині необхідно $ випромінювати подію з назвою input.


спочатку дякую за відповідь. чи можете ви, будь ласка, розширити або краще вказати на документи про подію "введення"? це здається побудованим у випадку.
Gal Ziv

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

1
Я опустив "значення" опори для компонента та створеної функції, і воно все ще працює. Ви можете пояснити, чому ви його використовували?
xetra11

1
Якщо ви не додасте опору, це буде undefinedдо першої зміни. Дивіться цю загадку, де я прокоментував props: ['value']. Зауважте, як початкове значення undefinedзамість hello: jsfiddle.net/asemahle/8Lrkfxj6 . Після першої зміни Vue динамічно додає опорному значенню компонента, тому він працює.
асемахле

Я читав прямо минуле цього в документах. Чудовий робочий приклад. +10, якби міг!
глина

121

З документації :

У Vue.js співвідношення компонент батько-дитина може бути узагальнено як підкріплення, події вгору. Батько передає дані дитині через реквізити, а дитина надсилає повідомлення батькові через події. Давайте подивимось, як вони працюють далі.

введіть тут опис зображення

Як передавати реквізит

Далі йде код для передачі реквізиту дочірньому елементу:

<div>
  <input v-model="parentMsg">
  <br>
  <child v-bind:my-message="parentMsg"></child>
</div>

Як випускати подію

HTML:

<div id="counter-event-example">
  <p>{{ total }}</p>
  <button-counter v-on:increment="incrementTotal"></button-counter>
  <button-counter v-on:increment="incrementTotal"></button-counter>
</div>

JS:

Vue.component('button-counter', {
  template: '<button v-on:click="increment">{{ counter }}</button>',
  data: function () {
    return {
      counter: 0
    }
  },
  methods: {
    increment: function () {
      this.counter += 1
      this.$emit('increment')
    }
  },
})
new Vue({
  el: '#counter-event-example',
  data: {
    total: 0
  },
  methods: {
    incrementTotal: function () {
      this.total += 1
    }
  }
})

5
що робити, якщо функція «збільшення» була у батьківському компоненті, і я хотів її запустити від дочірнього компонента?
Hamzaouiii

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

1
Я надрукую цю графіку і приклею її до голови. Дякую!
domih

1
У цьому прикладі, чи не повинен у нас кореневий компонент, визначений у кореневому компоненті? Щось на кшталт: `` `mount () {this.on ('приріст', () => {this.incrementTotal ();}); } `` `
jmk2142

94

У дочірніх компонентах:

this.$emit('eventname', this.variable)

У батьківському компоненті:

<component @eventname="updateparent"></component>

methods: {
    updateparent(variable) {
        this.parentvariable = variable
    }
}

3
Мій розум просто придушений цим прикладом. Ви не маєте поняття, скільки навчальних посібників я пройшов, перш ніж потрапити сюди ...
suchislife

18

Дочірній компонент

Використовуйте this.$emit('event_name')для надсилання події на батьківський компонент.

введіть тут опис зображення

Батьківський компонент

Для того, щоб прослухати цю подію в батьківському компоненті, ми робимо v-on:event_nameі метод ( ex. handleChange), який ми хочемо виконати на цій події

введіть тут опис зображення

Готово :)


13

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

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

Наскільки мені відомо, v-modelрішення підходить лише одному входу, який повертається до їхнього батьківського. Я зайняв небагато часу на пошуки, але документація Vue (2.3.0) дійсно показує, як синхронізувати кілька реквізитів, що надсилаються в компонент назад до батьківського (звичайно, через emit).

Це відповідним чином називається .syncмодифікатором.

Ось що говорить документація :

У деяких випадках нам може знадобитися "двостороння прив'язка" для опори. На жаль, справжнє двостороннє зв’язування може створити проблеми з технічним обслуговуванням, тому що дочірні компоненти можуть мутувати батьків, без того, щоб джерело цієї мутації було очевидним і для батьків, і для дитини.

Ось чому замість цього ми радимо випромінювати події за зразком update:myPropName. Наприклад, у гіпотетичній складовій з titleопорою ми можемо повідомити про намір присвоїти нове значення за допомогою:

this.$emit('update:title', newTitle)

Тоді батьків може прослухати цю подію та оновити властивість локальних даних, якщо цього хоче. Наприклад:

<text-document   
 v-bind:title="doc.title"  
 v-on:update:title="doc.title = $event"
></text-document>

Для зручності ми пропонуємо скорочення цього шаблону з модифікатором .sync:

<text-document v-bind:title.sync="doc.title"></text-document>

Ви також можете синхронізувати декілька одночасно, надсилаючи через об’єкт. Ознайомтеся з документацією тут


Це те, що я шукав. Дуже дякую.
Томас

Це найкраще та найсучасніше рішення станом на 2020 рік. Дякую!
Марсело

6

Шлях простіше - це використання this.$emit

Батько.вью

<template>
  <div>
    <h1>{{ message }}</h1>
    <child v-on:listenerChild="listenerChild"/>
  </div>
</template>

<script>
import Child from "./Child";
export default {
  name: "Father",
  data() {
    return {
      message: "Where are you, my Child?"
    };
  },
  components: {
    Child
  },
  methods: {
    listenerChild(reply) {
      this.message = reply;
    }
  }
};
</script>

Child.vue

<template>
  <div>
    <button @click="replyDaddy">Reply Daddy</button>
  </div>
</template>

<script>
export default {
  name: "Child",
  methods: {
    replyDaddy() {
      this.$emit("listenerChild", "I'm here my Daddy!");
    }
  }
};
</script>

Мій повний приклад: https://codesandbox.io/s/update-parent-property-ufj4b


5

Можна також передавати реквізити як Object або Array. У цьому випадку дані будуть двосторонніми:

(Це зазначається в кінці теми: https://vuejs.org/v2/guide/components.html#One-Way-Data-Flow )

Vue.component('child', {
  template: '#child',
  props: {post: Object},
  methods: {
    updateValue: function () {
      this.$emit('changed');
    }
  }
});

new Vue({
  el: '#app',
  data: {
    post: {msg: 'hello'},
    changed: false
  },
  methods: {
    saveChanges() {
        this.changed = true;
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>

<div id="app">
  <p>Parent value: {{post.msg}}</p>
  <p v-if="changed == true">Parent msg: Data been changed - received signal from child!</p>
  <child :post="post" v-on:changed="saveChanges"></child>
</div>

<template id="child">
   <input type="text" v-model="post.msg" v-on:input="updateValue()">
</template>



0

1) Дочірній комплімент: ви можете використовувати так у дочірньому компоненті, пишіть так: this.formValue - це надіслати деякі дані батьківському компоненту

this.$emit('send',this.formValue)

2) Parrent Compnenet: і в тезі parrent компонент отримують змінну так: і це код для отримання даних дочірньої компенсації в батьківському тезі компонента

@send="newformValue"

0

Інший спосіб - передати посилання вашого сетера від батьків як опору на дочірній компонент, аналогічно тому, як вони це роблять в React. Скажімо, у вас є метод updateValueна батьку для поновлення значення, можна створити екземпляр компонента дитини наступним чином: <child :updateValue="updateValue"></child>. Тоді на дитину ви будете мати відповідну опору: props: {updateValue: Function}і в шаблоні виклику методу при зміні вхідного: <input @input="updateValue($event.target.value)">.


0

Не знаю чому, але я просто успішно оновив батьківські дані, використовуючи дані як об’єкт, :set&computed

Parent.vue

<!-- check inventory status - component -->
    <CheckInventory :inventory="inventory"></CheckInventory>

data() {
            return {
                inventory: {
                    status: null
                },
            }
        },

Child.vue

<div :set="checkInventory">

props: ['inventory'],

computed: {
            checkInventory() {

                this.inventory.status = "Out of stock";
                return this.inventory.status;

            },
        }

0

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

Спочатку визначте eventBus як новий Vue.

//main.js
import Vue from 'vue';
export const eventBus = new Vue();

Pass your input value via Emit.
//Sender Page
import { eventBus } from "../main";
methods: {
//passing data via eventbus
    resetSegmentbtn: function(InputValue) {
        eventBus.$emit("resetAllSegment", InputValue);
    }
}

//Receiver Page
import { eventBus } from "../main";

created() {
     eventBus.$on("resetAllSegment", data => {
         console.log(data);//fetching data
    });
}

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