У меня есть родительский компонент куда в data() я хочу передать вычисляемое свойство из дочерного компонента.
// ParentComponent.vue
<template>
<div>
<ChildComponent :parent="parent"/>
</div>
</template>
<script>
import ChildComponent from '@/components/ChildComponent';
export default {
name: 'ParentComponent',
components: {
ChildComponent,
},
data() {
return {
parent: {
parentData: '',
}
};
},
</script>
В дочернем компоненте вычисляемое свойство это конкатенация строк, полей свойства data дочерного компонента.
<template>
<div>
<div class="control">
<input
v-model="salarySumm"
class="input"
type="text"
placeholder="Add the estimated salary (1000 - 2000)"
>
<div class="select">
<select v-model="salaryCode">
<option disabled value>Select currency</option>
<option
v-for="(el, index) in currency"
:key="index"
:value="el.code"
>{{ el.title }}</option>
</select>
</div>
</div>
</template>
<script>
export default {
name: 'ChildComponent',
props: ['parent'],
data() {
return {
salarySumm: '',
salaryCode: '',
}
},
created() {
// Тут я получаю массив объектов в виде
// [{ title: 'string', code: 'string' }]
this.$store.dispatch('getCurrencies')
},
computed: {
currency() {
let list = this.$store.getters.currencies.data
const currencyList = []
if (list !== undefined) {
for (const el of list) {
if (el.code == '(none)' || el.code == null) el.code = ''
if (el.name !== null) {
currencyList.push({ title: `${el.name} | ${el.code}`, code: el.code })
}
}
}
return currencyList
},
salary() {
// Здесь я получаю строку в том виде, в котором хочу передать ее в родительский компонент.
return this.salarySumm + ' ' + this.salaryCode
},
</script>
Как мне передать вычисляемое свойство salary в родительский компонент, а точнее в parent.parentData?
В дочерние компоненты вы передаёте свойства, а из дочерних в родительский вызываете события $emit
по изменению свойств:
// Родительский компонент.
<template>
<child-component :someProps="parent" @updateParent="onUpdateSalary" />
</template>
<script>
export default {
// Локальные данные компонента.
data() {
return {
parent: {
parentData: '',
}
}
},
methods: {
onUpdateSalary(someData) {
// Выполняем необходимые действия с `someData`
}
}
}
</script>
// Дочерний компонент.
<template>
<button type="button" @click="doSomething">Передать родителю</button>
</template>
<script>
export default {
// Входящие свойства из родителя.
props: ['someProps'],
// Локальные данные компонента.
data() {
return {
amount: '',
code: '',
}
},
methods: {
doSomething() {
this.$emit('updateParent', {
amount: this.amount,
code: this.code
})
}
}
}
</script>
Если нужно передать вычисляемое свойство computed
родительскому компоненту, то необходимо использовать наблюдатель watch
, которому задаётся функция обработчик вызываемая при каждом изменении вычисляемого (и не только) свойства. Пример ниже.
// Отключим ненужные для примера
// сообщения в консоли.
Vue.config.productionTip = false
Vue.config.devtools = false
Vue.component('parent-component', {
template: `
<div>
<child-component :someProps="parent" @updateParent="onUpdateSalary" />
<p v-if="parentSalary">
Значение <b>parentSalary</b>, отображаемое в "parent-component": <pre>{{ parentSalary }}</pre>
</p>
</div>`,
data() {
return {
// Родительское значение.
parentSalary: null,
parent: {
parentData: '... из поля data',
}
}
},
methods: {
onUpdateSalary(data) {
this.parentSalary = data
}
}
})
Vue.component('child-component', {
template: `
<div>
<pre>Свойство из родительского компонента: {{ someProps.parentData }}</pre>
<div v-if="currencies.length" class="control">
<input type="number" v-model.number="amount" class="input" />
<select v-model="code">
<option disabled selected>Select currency</option>
<template v-for="(currency, index) in currencies">
<option :key="index" :value="currency.code">{{ currency.name }}</option>
</template>
</select>
</div>
<div v-else>Нет данных для отображения.</div>
<p v-if="salary">Значение <b>salary</b>, отображаемое в "child-component": <b>{{ salary }}</b></p>
</div>`,
props: ['someProps'],
data() {
return {
amount: '',
code: '',
currencies: [{
"code": "EUR",
"name": "Euro"
},
{
"code": "GBP",
"name": "U.K. Pound Sterling"
},
{
"code": "JPY",
"name": "Japanese Yen"
}
]
}
},
computed: {
salary() {
return this.amount + ' ' + this.code
}
},
watch: {
// Эта функция запускается при любом изменении значений
// в вычисляемом свойстве `salary`.
salary(newValue, oldValue) {
// Пробрасываем данные родительскому компоненту,
// ч/з вызов метода.
this.$emit('updateParent', {
amount: this.amount,
code: this.code
})
}
}
})
new Vue({
el: '#app',
data: {}
})
<div id="app">
<div>
<parent-component></parent-component>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10"></script>
computed: {
salary() {
return this.amount + ' ' + this.code
}
},
watch: {
// Эта функция запускается при любом изменении значений
// в вычисляемом свойстве `salary`.
salary(newValue, oldValue) {
// Пробрасываем данные родительскому компоненту,
// ч/з вызов метода.
this.$emit('updateParent', {
amount: this.amount,
code: this.code
})
}
}
Судя по коду this.$store.dispatch
вы используете vuex
. В таком случае, можно всю логику перенести в хранилище. Это освободит от постоянных проверок полученных данных в каждом компоненте.
// Отключим ненужные для примера
// сообщения в консоли.
Vue.config.productionTip = false
Vue.config.devtools = false
// Список доступных мутаций (сеттеры).
const CASH_INIT = 'CASH_INIT'
const CASH_SET_AMOUNT = 'CASH_SET_AMOUNT'
const CASH_SET_CODE = 'CASH_SET_CODE'
const store = new Vuex.Store({
state: {
amount: null,
code: null,
currencies: []
},
getters: {
amount: state => state.amount,
code: state => state.code,
currencies: state => state.currencies,
salary: state => {
if (state.amount && state.code) {
return state.amount + ' ' + state.code
}
return null
}
},
// Обработчики мутаций обязаны быть синхронными.
mutations: {
[CASH_INIT](state, currencies) {
state.currencies = currencies
},
[CASH_SET_AMOUNT](state, amount) {
amount = parseFloat(amount)
if (!isNaN(amount)) {
state.amount = amount
} else {
state.amount = ''
alert('Ошибка в типе данных: `amount`.')
}
},
[CASH_SET_CODE](state, code) {
code = code.toString()
if (code) {
state.code = code
} else {
state.code = ''
alert('Ошибка в типе данных: `code`.')
}
}
},
actions: {
getCurrencies(context) {
try {
// В своем проекте используйте `fetch` или axios, например.
// const response = await fetch('http://www.floatrates.com/daily/usd.json')
// const data = await response.json()
// context.commit('CASH_INIT', data)
context.commit('CASH_INIT', [{
"code": "EUR",
"name": "Euro"
}, {
"code": "GBP",
"name": "U.K. Pound Sterling"
}, {
"code": "JPY",
"name": "Japanese Yen"
}, {
"code": "AUD",
"name": "Australian Dollar"
}, {
"code": "CAD",
"name": "Canadian Dollar"
}, {
"code": "CHF",
"name": "Swiss Franc"
}, {
"code": "UYU",
"name": "Uruguayan Peso"
}])
} catch (e) {
console.log(e)
}
},
calculate(context, {
amount,
code
}) {
context.commit('CASH_SET_AMOUNT', amount)
context.commit('CASH_SET_CODE', code)
},
}
})
Vue.component('child-component', {
template: `
<div>
<div v-if="currencies.length" class="control">
<input type="number" v-model.number="amount" class="input" />
<select v-model="code">
<option disabled selected>Select currency</option>
<template v-for="(currency, index) in currencies">
<option :key="index" :value="currency.code">{{ currency.name }}</option>
</template>
</select>
<button type="button" @click="send">Отправить данные во vuex</button>
</div>
<div v-else>Нет данных для отображения.</div>
</div>`,
data() {
return {
amount: '',
code: '',
}
},
computed: {
// При компонентном подходе ...mapGetters()
...Vuex.mapGetters(['currencies']),
},
mounted() {
this.getCurrencies()
},
methods: {
// При компонентном подходе ...mapActions().
// Теперь в компоненте доступны методы:
// this.getCurrencies() и this.calculate(...)
...Vuex.mapActions(['getCurrencies', 'calculate']),
send() {
this.calculate({
amount: this.amount,
code: this.code
})
}
}
})
Vue.component('parent-component', {
template: `
<div>
<child-component />
<p v-if="salary">Значение <b>salary</b>, отображаемое в "parent-component": <b>{{ salary }}</b></p>
</div>`,
data() {
return {}
},
computed: {
...Vuex.mapGetters([
'salary',
]),
},
})
new Vue({
el: '#app',
store,
data: {},
// Используя vuex получаем доступ к хранилище
// не только в дочерних и родительских компонентах.
computed: {
...Vuex.mapGetters([
'salary',
]),
},
})
<div id="app">
<div>
<p v-if="salary">Значение <b>salary</b> в корне приложения: <b>{{ salary }}</b></p>
<parent-component></parent-component>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10"></script>
<script src="https://unpkg.com/vuex@2.0.0"></script>
Посмотрите другие примеры в этих ответах 984023, 975447.
Немного разобравшись с темой хочу дать и свой ответ.
Первый вариант: Но этот вариант имеет один недостаток, если не отображать результат вычисляемого свойства в компоненте, то мы не будем "дергать" его и оно не будет вычисляться. Т.е. этот метод годится только если вы хотите в этом же методе и использовать вычисляемое свойство например так
<div>Salary: {{ salary }}</div>
Я покажу как можно прокинуть данные в родителя с помощью $emit но без якобы использования события. Все дело в том, что $emit и есть наше собственное событие. Вот полезная статья про Пользовательские события.
В дочернем компоненте, я использую computed потому что он всегда пересчитывает данные которые меняются и сам факт пересчета и есть в неком смысле событие. Computed кроме возврата значения может создает некие побочные эффекты, запись данных куда то в данном случает в событие input (см. computed ниже). Останется только прописать пропс в дочернем компоненте и именем value.
// ChildComponent.vue
<script>
export default {
name: 'ChildComponent',
props: ['value'],
data() {
return {
salarySumm: '',
salaryCode: '',
}
},
created() {
this.$store.dispatch('getCurrencies')
},
computed: {
currency() {
let list = this.$store.getters.currencies.data
const currencyList = []
if (list !== undefined) {
for (const el of list) {
if (el.code == '(none)' || el.code == null) el.code = ''
if (el.name !== null) {
currencyList.push({ title: `${el.name} | ${el.code}`, code: el.code })
}
}
}
return currencyList
},
salary() {
let salary = this.salarySumm + ' ' + this.salaryCode
this.$emit('input', salary)
return salary
},
</script>
и далее в родителе получить эти данные через v-model
// ParentComponent.vue
<template>
<div>
<ChildComponent v-model="parent.parentData"/>
</div>
</template>
<script>
import ChildComponent from '@/components/ChildComponent';
export default {
name: 'ParentComponent',
components: {
ChildComponent,
},
data() {
return {
parent: {
parentData: '',
}
};
},
</script>
Второй вариант. Я использую внутренние события для того что бы вызвать метод который создать пользовательское событие $emit
// ParentComponent.vue
// Все то же самое что и в варианте 1
// ChildComponent.vue
// В шаблоне я явно привязываю событие к моему методу который склеит строки и создаст пользовательское событие. Этот метод в варианте 1 был вычисляемым свойством.
div class="control">
<input
v-model="salarySumm"
class="input"
type="text"
placeholder="Add the estimated salary (1000 - 2000)"
v-on:input="salary"
>
<div class="select">
<select v-model="salaryCode" @change="salary()">
<option disabled value>Select currency</option>
<option
v-for="(el, index) in currency"
:key="index"
:value="el.code"
>{{ el.title }}</option>
</select>
</div>
</div>
// Вместо вычисляемого свойства computed я создаю метод
methods: {
salary() {
this.$emit('input', this.salarySumm + ' ' + this.salaryCode)
},
}
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Помогите пожалуйста решить вопрос с табамиПри навешивание click на общий div в котором есть картинка и ссылка, переключение таба срабатывает...
Есть форма, в ней сотрудник вводит данные, в конце нажимает на кнопку и создаетсяhtm файл
я свсем новичек в vue, столкнулся с маленькой проблемой, буду рад любой помощиЯ использую библиотеку vue-scrollto, в документации нашел описаный...
Ка сделать так чтобы по ид создавались новые canvas в зависимости от текста то есть Внутри canvas прозрачный цвет только граница с градиентом нужна