Условный реквизит в Vue2
У меня есть случай использования, когда мне может понадобиться передать объект props
в качестве реквизита дочернему компоненту.
Проблема возникла, когда я рефакторировал компонент, чтобы сделать как форму, так и таблицу субкомпонентами родительского элемента. Теперь форма будет $emit
событие своему родителю, который выполнит асинхронную операцию и передаст результат в виде props
в таблицу. Это сработало нормально, но когда пользователь нажимает кнопку "Вернуться к форме", форма повторно визуализируется, тем самым возвращая свое состояние к исходному значению.
Я пытался сохранение содержания формы в родителе и передача его обратно в форму в виде props
, но это привело к проблеме первоначальной установки значений. Я не хотел напрямую мутировать реквизит, поэтому попробовал такой подход:
FormContainer.vue
<template>
<div v-if="formShown">
<form-component :initialValues="formValues" @formSubmitted="displayResults"></form-component>
</div>
<div v-if="tableShown">
<table-component :results="fetchedResults" @returnToForm="returnToForm"></table-component>
</div>
</template>
<script>
export default {
data(){
return{
formShown: true,
tableShown: false,
formValues:{
address1: '',
address2: '',
address3: '',
country: ''
},
fetchedResults: []
}
},
methods:{
async displayResults(){
this.fetchedResults = await someAsynchronousCall();
this.formShown = false;
this.tableShown = true;
},
returnToForm(){
this.tableShown = false;
this.formShown = true;
}
}
}
</script>
Компонент формы.vue
<template>
<!--Some form fields here, bound to data(){}, ommitted for brevity-->
</template>
<script>
export default{
props:['initialValues'],
data(){
return{
//Original structure
/*selectedAddress:{
address1: '',
address2: '',
address3: '',
country: ''
}*/
selectedAddress: JSON.parse(JSON.stringify(this.initialValues)) //Object.assign({}, this.initialValues) //tried both of these for deep copy
}
}
}
</script>
Проблема с этим заключалась в том, что при первоначальном создании компонента ему передается пустой объект (или, скорее, объект только с пустыми свойствами, я предполагаю, что они равны одному и тому же thing), что означает, что мои свойства данных инициализируются как undefined
.
Затем я попытался использовать тернарный код для инициализации объекта данных со значениями в реквизитах или пустой строке, например:
data(){
return{
selectedAddress: {
address1: this.initialValues.address1 ? this.initialValues.address1 :'',
address2: this.initialValues.address2 ? this.initialValues.address2 :'',
address3: this.initialValues.address3 ? this.initialValues.address3 :'',
country:this.initialValues.country ? this.initialValues.country :''
}
}
},
Но это приводит к ошибкам, говорящим, что selectedAddress
не определен в экземпляре, но ссылается во время рендеринга. Я предполагаю, что это означает, что использование троичного для инициализации реквизитов неправильно.
Затем я попытался проверить наличие реквизитов в крюке жизненного цикла mounted(){}
и установить данные свойства там, как это:
mounted(){
if(!this.initialValues || _.isEmpty(this.initialValues)){
Logger.info(`no props passed, no setup needed`);
return;
}
if(this.initalValues.address1){
Logger.info(`setting address 1`);
this.selectedAddress.address1 = this.initalValues.address1;
}
if(this.initialValues.address2){
Logger.info(`setting address 2`);
this.selectedAddress.address2 = this.initalValues.address2;
}
if(this.initialValues.address3){
Logger.info(`setting address 3`);
this.selectedAddress.address3 = this.initalValues.address2;
}
if(this.initialValues.country){
Logger.info(`setting country`);
this.selectedAddress.country = this.initalValues.country;
}
}
Это работает для первого запуска формы, но this.initialValues
всегда undefined
, когда компонент монтируется. Я проверил состояние компонента и обнаружил, что initialValues
существует, но не во время крючка жизненного цикла mounted
. В любом случае такой подход казался "халтурным".
Может ли кто-нибудь направить меня к более Vue способу достижения этого?
1 ответ:
Я думаю, что вы должны быть в состоянии разобраться в этом, переместив код в FormComponent из данных в вычисляемые.
Вычисляемые свойства являются реактивными, поэтому, когда initialValues prop в конечном итоге получает некоторые данные, форма должна отражать их.
Объект.назначение будет работать, если initialValues всегда имеет структуру address1, address2 и т. д. (Даже если они изначально равны нулю). Если нет (то есть initialValues initially = {}, вам придется выполнить проверку свойств по свойствам.<template> <!--Some form fields here, bound to data(){}, ommitted for brevity--> <div>selectedAddress.address1</div> <div>selectedAddress.address2</div> </template> <script> export default{ props:['initialValues'], data(){ }, computed: { selectedAddress() { return Object.assign({}, this.initialValues) } } } </script>
Чтобы сохранить форму значения для следующего раунда вам нужно передать selectedAddress вверх через emit и применить их к родительским formValues.
Альтернатива, которая мне очень нравится, - это Vuex, где форма получает свое начальное значение из магазина и отправляет его обратно в магазин через действие, которое запускает асинхронную работу и обновляет магазин результатами. По сути, ваш метод displayResults становится действием, но вам не нужна асинхронность..ждать.
Vuex формализует шаблон потока данных, чтобы вам было проще рассуждайте об этом и отлаживайте вещи. По существу, вам не нужно беспокоиться о передаче данных в реквизит и через Эмит.
Еще одна мысль, вы можете избежать сброса значений FormComponent, используя v-show вместо v-if в FormContainer. Это должно сохранить форму div (и это состояние), но скрыть его. Тогда вам, возможно, вообще не нужно будет иметь значения formValues на parent.
<template> <div v-show="formShown"> <form-component :initialValues="formValues" @formSubmitted="displayResults"></form-component> </div> <div v-show="tableShown"> <table-component :results="fetchedResults" @returnToForm="returnToForm"></table-component> </div> </template>