Vuejs 2: Unable to switch between tabbed form - vue.js

I am using Vue2 for buidling a tab-based form. I am using in my main.js
import VueFormWizard from 'vue-form-wizard'
import 'vue-form-wizard/dist/vue-form-wizard.min.css'
Vue.use(VueFormWizard)
import VeeValidate from 'vee-validate'
Vue.use(VeeValidate)
The file AddUser.vue comprises of the following code:
<script>
import swal from 'sweetalert'
export default {
methods: {
validateFirstTab: function () {
this.$validator.validateAll().then((result) => {
if (result) {
return
}
swal('Input Field(s) Validation', 'Please correct the errors!', 'error')
})
}
}
}
</script>
<template>
<div class="wrapper" id="add-user-wrapper">
<section class="content">
<form-wizard #on-complete="onComplete"
shape="tab"
color="#3498db"
error-color="#a94442">
<h2 slot="title">Add a New User</h2>
<tab-content title="User Details" :before-change="validateFirstTab">
<div class="row">
<div class="col-md-12">
<div class="col-md-4">
<div class="form-group">
<label class="control-label">Name</label>
<input v-model="user.name" v-validate data-vv-rules="required|alpha_spaces" data-vv-delay="500" data-vv-as="Name" class="form-control" :class="{'input': true, 'is-danger': errors.has('name') }" type="text" placeholder="Enter Name" name="name" autofocus="true" />
<i v-show="errors.has('name')" class="fa fa-warning"></i>
<span v-show="errors.has('name')" class="help is-danger">{{ errors.first('name') }}</span>
</div>
</div>
</form-wizard>
</section>
</div>
<template>
The problem that I am facing is whenever I am trying to validate the input field it is getting validated correctly but throwing an error on console: "cannot read property then of undefined" while switching to the new tab. I searched through the communities only to get back a solution of returning a Promise with resolve(true) always but still unfortunately, even with a valid input in the first tab, I am unable to switch to the next tab(code not given in the html below).
Can someone help me out in this regard as to what or how should be the approach? As I am quite new to Vue, please let me know if you need any further details

A return value is missing in the validator promise. vue-form-wizard beforeChange function is expecting a boolean.
Here's the TabContent component's beforeChange prop.
/***
* Function to execute before tab switch. Return value must be boolean
* If the return result is false, tab switch is restricted
*/
beforeChange: {
type: Function
},
Here's what actually happens when the promise is resolved.
validateBeforeChange (promiseFn, callback) {
this.setValidationError(null)
// we have a promise
if (isPromise(promiseFn)) {
this.setLoading(true)
promiseFn.then((res) => {
// ^^^
// res is undefined because there is no return value in your case,
// error is catched later on.
//
this.setLoading(false)
let validationResult = res === true
this.executeBeforeChange(validationResult, callback)
}).catch((error) => {
this.setLoading(false)
this.setValidationError(error)
})
// we have a simple function
} else {
let validationResult = promiseFn === true
this.executeBeforeChange(validationResult, callback)
}
},
Try the following:
<script>
import swal from 'sweetalert'
export default {
methods: {
validateFirstTab: function () {
this.$validator.validateAll().then((result) => {
if (result) {
return true
}
swal('Input Field(s) Validation', 'Please correct the errors!', 'error')
return false
})
}
}
}
</script>

Related

ReferenceError: contenuto_translated is not defined

I'm stuck at this error that I get only if I run my laravel + vuejs in build mode.
Ihave a custom component with 3 input and I'm using it in a Quasar dialog. When I start typing inside one of these 3 inputs, it give me the error I wrote in the post title.
See code and details.
I have a custom component file (EditObjectTranslation_Singleline.vue)
<template>
<q-dialog ref="dialogRef" #hide="onDialogHide">
<q-card class="q-dialog-plugin">
<!--
...content
... use q-card-section for it?
-->
<q-card-section>
<div class="text-h6">{{ titolo }}</div>
<q-form #submit.prevent="invia_form">
<div>
<q-input outlined v-model="contenuto_campo" label="Contenuto CAMPO" class="mt-4 block w-full" :disable="true"/>
</div>
<div>
<q-input outlined v-model="contenuto_lingua_default" label="Contenuto Lingua IT" class="mt-4 block w-full" :disable="true"/>
</div>
<div>
<q-input outlined v-model="contenuto_translated" :label="label_translated" class="mt-4 block w-full" :maxlength="max_lunghezza"/>
</div>
</q-form>
</q-card-section>
<!-- buttons example -->
<q-card-actions align="right">
<q-btn color="primary" label="OK" #click="onOKClick" />
<q-btn color="primary" label="Cancel" #click="onDialogCancel" />
</q-card-actions>
</q-card>
</q-dialog>
</template>
<script setup>
import { useDialogPluginComponent } from 'quasar'
import { computed } from '#vue/reactivity'
const props = defineProps({
contenuto_campo: '',
contenuto_lingua_default: '',
contenuto_translated: '',
lingua: '',
max_lunghezza: 0
})
const label_translated = computed (() => {
return "Contenuto tradotto " + props.lingua
})
defineEmits([
// REQUIRED; need to specify some events that your
// component will emit through useDialogPluginComponent()
...useDialogPluginComponent.emits
])
const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent()
// dialogRef - Vue ref to be applied to QDialog
// onDialogHide - Function to be used as handler for #hide on QDialog
// onDialogOK - Function to call to settle dialog with "ok" outcome
// example: onDialogOK() - no payload
// example: onDialogOK({ /*...*/ }) - with payload
// onDialogCancel - Function to call to settle dialog with "cancel" outcome
// this is part of our example (so not required)
function onOKClick () {
// on OK, it is REQUIRED to
// call onDialogOK (with optional payload)
onDialogOK(props)
// or with payload: onDialogOK({ ... })
// ...and it will also hide the dialog automatically
}
</script>
Then, I want to use the previous component as a custom component in a quasar dialog.
So the page code where I'm using the previous custom components follows
<script setup>
import AuthenticatedLayout from '#/Layouts/AuthenticatedLayout.vue';
import { Inertia } from '#inertiajs/inertia';
import { useQuasar } from 'quasar';
import { ref } from 'vue';
import { computed } from '#vue/reactivity';
import { Link } from '#inertiajs/inertia-vue3';
import CustomComponent from '#/Components/EditObjectTranslation_Singleline.vue';
import axios from 'axios';
const $q = useQuasar()
const props = defineProps({
catalogues: Array,
lingue: Array,
field2betranslated: Number
})
const $qTranslate = useQuasar()
function onTranslateObject (pLingua) {
if (selected.value.length > 0 ) {
axios
.get('/api/getTraduzioni/'+ pLingua + '/' + selected.value[0].resource_id)
.then((response) => {
$q.dialog({
component: CustomComponent,
componentProps: {
titolo: 'Traduci contenuti:',
contenuto_campo: selected.value[0].name,
contenuto_lingua_default: response.data.originale,
contenuto_translated: response.data.tradotto,
lingua: pLingua,
max_lunghezza: 64,
resource_id: selected.value[0].resource_id
// ...more..props...
}
}).onOk((formData) => {
// console.log('>>>> OK')
translateObject(formData)
}).onOk(() => {
// console.log('>>>> second OK catcher')
}).onCancel(() => {
// console.log('>>>> Cancel')
}).onDismiss(() => {
// console.log('I am triggered on both OK and Cancel')
})
})
} else {
$q.dialog({
title: 'Attenzione',
message: 'Devi selezionare una riga'
})
}
}
</script>
<template>
<Head :title="$t('cataloghi.pagetitle') " />
<AuthenticatedLayout>
<div class="q-py-xl">
<!-- Pulsanti traduzioni -->
<div class="w-full flex justify-end space-x-2 mb-4">
<q-btn v-for="lingua in lingue" :label="lingua.codiceIso" :key="lingua.codiceIso" #click="onTranslateObject(lingua.codiceIso)" icon="language" color="whte" text-color="text-grey-7" />
</div>
</div>
</AuthenticatedLayout>
</template>
When I open the dialog and try to input in the "contenuto_translated" q-input I get this error:
"ReferenceError: contenuto_translated is not defined"
Surely I'm missing something.
Please help me.

Unable to Define Variable in Vue

I'm just starting to use VueJS & Tailwind, having never really used anything related to npm before.
I have the below code, making use of Tailwind & Headless UI which through debugging, I know I'm like 99% of the way there... except for the continuous error message
Uncaught ReferenceError: posts is not defined
I know this should be straight forward, but everything I've found either here or with Google hasn't worked. Where am I going wrong?
<template>
<Listbox as="div" v-model="selected">
<ListboxLabel class="">
Country
</ListboxLabel>
<div class="mt-1 relative">
<ListboxButton class="">
<span class="">
<img :src="selected.flag" alt="" class="" />
<span class="">{{ selected.name }}</span>
</span>
<span class="">
<SelectorIcon class="" aria-hidden="true" />
</span>
</ListboxButton>
<transition leave-active-class="" leave-from-class="opacity-100" leave-to-class="opacity-0">
<ListboxOptions class="">
<ListboxOption as="template" v-for="country in posts" :key="country" :value="country" v-slot="{ active, selected }">
<li :class="">
<div class="">
<img :src="country.flag" alt="" class="" />
<span :class="[selected ? 'font-semibold' : 'font-normal', 'ml-3 block truncate']">
{{ country.latin }}
</span>
</div>
<span v-if="selected" :class="">
<CheckIcon class="" aria-hidden="true" />
</span>
</li>
</ListboxOption>
</ListboxOptions>
</transition>
</div>
</Listbox>
</template>
<script>
import { ref } from 'vue'
import { Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions } from '#headlessui/vue'
import { CheckIcon, SelectorIcon } from '#heroicons/vue/solid'
import axios from 'axios'
export default {
data() {
return {
response: null,
posts: undefined,
};
},
components: {
Listbox,
ListboxButton,
ListboxLabel,
ListboxOption,
ListboxOptions,
CheckIcon,
SelectorIcon,
},
mounted: function() {
axios.get('http://localhost')
.then(response => {
this.posts = response.data;
});
},
setup() {
const selected = ref(posts[30])
return {
selected,
}
},
}
</script>
The offending line is const selected = ref(posts[30]) which I know I need to somehow define posts, but I don't get how?
CAUSE OF YOUR ERROR:
You are trying to access an array element before the array is populated. Thus the undefined error.
EXPLANATION
You are using a mix of composition api and options api. Stick to one.
I am writing this answer assuming you will pick the composition api.
Follow the comments in the below snippet;
<script>
// IMPORT ONMOUNTED HOOK
import { ref, onMounted } from 'vue'
import { Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions } from '#headlessui/vue'
import { CheckIcon, SelectorIcon } from '#heroicons/vue/solid'
import axios from 'axios'
export default {
// YOU DO NOT NEED TO DEFINE THE DATA PROPERTY WHEN USING COMPOSITION API
/*data() {
return {
response: null,
posts: undefined,
};
},*/
components: {
Listbox,
ListboxButton,
ListboxLabel,
ListboxOption,
ListboxOptions,
CheckIcon,
SelectorIcon,
},
// YOU DO NOT NEED THESE LIFE CYCLE HOOKS; COMPOSITION API PROVIDES ITS OWN LIFECYCLE HOOKS
/*mounted: function() {
axios.get('http://localhost')
.then(response => {
this.posts = response.data;
});
},*/
setup() {
// YOU ARE TRYING TO ACCESS AN ELEMENT BEFORE THE ARRAY IS POPULATED; THUS THE ERROR
//const selected = ref(posts[30])
const posts = ref(undefined);
const selected = ref(undefined);
onMounted(()=>{
// CALL THE AXIOS METHOD FROM WITHIN THE LIFECYCLE HOOK AND HANDLE THE PROMISE LIKE A BOSS
axios.get('http://localhost')
.then((res) => {
selected.value = res[30];
});
});
return {
selected,
}
},
}
</script>
According to your comment; you should first check if the “selected != null” before using ‘selected’ inside the template. You can use a shorthand version like this
<img :src=“selected?.flag” />

Vue JS: Forms are not getting cleared when router.push() method is executed after the logout

I am using Flask as a backend and Vue JS as front end for my development. Vuex for state store.
In the logout() I am clearing the authentication token from localStorage via store.dispatch('/logout') and then using router.push('/login') to navigate to Login.vue. I find that the form details entered before are not getting cleared. When the logout() is performed it navigates to the Login.vue with flash message stating 'You have been logged out successfully'.
Below is the code snippet for the same:
logout() {
axios.get(`${this.host}:5000/logout`)
.then((res) => {
this.$store.dispatch('logout')
.then(() => {
this.$router.push('/login');
});
this.flashMessage.success({
message: res.data.msg,
time: 5000,
flashMessageStyle: {
backgroundColor: 'linear-gradient(#e66465, #9198e5)',
},
});
})
.catch((error) => {
this.flashMessage.error({
message: error.toString(),
time: 5000,
flashMessageStyle: {
backgroundColor: 'linear-gradient(#e66465, #9198e5)',
},
});
});
}
logout() is written in App.vue. Here router is an instance of vue-router.
To avoid the issue of form data not being cleared I have used router.go() instead of router.push() in the above code. But because of this implementation, the flash message is not getting displayed as the reload (because of router.go()) is overwriting the displaying of flash message which I don't want.
Please let me if it is possible to erase the form data after logout without getting into the trouble of not showing flash message.
Not sure how you have implemented Login.vue, but here is a Login.vue that I have implemented, and it works. One of the takeaways should be that my form model data values are initialized to empty strings.
<template>
<div id="login">
<form class="form-horizontal">
<div class="form-group">
<label for="username" class="col-md-offset-3 col-md-2 align-right">User Name</label>
<div class="col-md-3">
<input type="input" ref="username" class="form-control" id="username" v-model="username">
</div>
</div>
<div class="form-group">
<label for="password" class="col-md-offset-3 col-md-2 align-right">Password</label>
<div class="col-md-3">
<input type="password" class="form-control" id="password" v-model="password" v-on:keyup.enter="login">
</div>
</div>
<div class="form-group">
<div class="col-md-offset-5 col-md-4">
<button type="button" class="btn btn-default"
v-on:click="login">Login</button>
<span class="error-msg" v-if="errorMsg">{{ errorMsg }}</span>
</div>
</div>
</form>
</div>
</template>
<script>
import { loginUrl, axios, processAjaxLoginError } from '../globalvars.js'
export default {
name: 'Login',
data() {
return {
username: '',
password: '',
errorMsg: ''
}
},
methods: {
login() {
axios.post(loginUrl, {
username: this.username,
password: this.password
})
.then(response => {
// Commit the token to the store
this.$store.commit('updateToken', { token: response.data.message });
// Clear error message
this.errorMsg = '';
// Redirect to customer index view
this.$router.push("/customers")
})
.catch(error => {
this.errorMsg = processAjaxLoginError(error);
})
}
},
computed: {
token() {
return this.$store.state.token;
}
},
mounted() {
this.$refs.username.focus();
}
}
</script>

v-if not working for form validation and displaying errors

I'm trying to validate a simple form that contains two fields:
A select box
A file field
If one of the fields aren't filled in, a div (containing an error label) should be rendered next to the corresponding input field.
The problem: My 'error divs' aren't rendered when pushing data to the errors object (if the form is invalid).
Please note my console.log statement, that tells me that my error object has a key 'file' and a key 'selectedSupplier'.
Side note: I'm following this example: https://v2.vuejs.org/v2/cookbook/form-validation.html
Differences are, that I'd like to show error labels next to the corresponding field and that I'm setting errors in my errors object, instead of a simple array. So what could I be doing wrong?
Thanks.
This is my Main.vue file:
<template>
<div>
<form #submit="upload">
<div class="mb-8">
<h1 class="mb-3 text-90 font-normal text-2xl">Import Order Csv</h1>
<div class="card">
<div class="flex border-b border-40">
<div class="w-1/5 px-8 py-6">
<label for="supplier_id" class="inline-block text-80 pt-2 leading-tight">Supplier</label>
</div>
<div class="py-6 px-8 w-1/2">
<select v-model="selectedSupplier" id="supplier_id" name="supplier_id" ref="supplier_id" class="w-full form-control form-input form-input-bordered">
<option v-for="supplier in suppliers" v-bind:value="supplier.id">{{ supplier.name }}</option>
</select>
<div v-if="errors.hasOwnProperty('selectedSupplier')" class="help-text error-text mt-2 text-danger">
Required.
</div>
</div>
</div>
<div class="flex border-b border-40">
<div class="w-1/5 px-8 py-6">
<label for="csv_file" class="inline-block text-80 pt-2 leading-tight">File</label>
</div>
<div class="py-6 px-8 w-1/2">
<input id="csv_file" type="file" name="file" ref="file" #change="handleFile">
<div v-if="errors.hasOwnProperty('file')" class="help-text error-text mt-2 text-danger">
Required.
</div>
</div>
</div>
</div>
</div>
<div class="flex items-center">
<button type="submit" class="btn btn-default btn-primary inline-flex items-center relative">Import</button>
</div>
</form>
</div>
</template>
<script>
export default {
mounted() {
this.listSuppliers();
},
data() {
return {
errors: [],
file: '',
suppliers: [],
};
},
methods: {
checkForm() {
if (!this.selectedSupplier) {
this.errors.selectedSupplier = 'Supplier required';
}
if (!this.file) {
this.errors.file = 'File required';
}
},
listSuppliers() {
const self = this;
Nova.request()
.get('/tool/import-order-csv/suppliers')
.then(function (response) {
self.suppliers = response.data.data;
})
.catch(function (e) {
self.$toasted.show(e, {type: "error"});
});
},
handleFile: function (event) {
this.file = this.$refs.file.files[0];
},
upload: function (event) {
this.checkForm();
if (this.errors.hasOwnProperty('selectedSupplier') || this.errors.hasOwnProperty('file')) {
console.log(this.errors); // this actually shows both errors!
event.preventDefault();
}
const formData = new FormData();
formData.append('file', this.file);
formData.append('supplier_id', this.$refs.supplier_id.value);
const self = this;
Nova.request()
.post('/tool/import-order-csv/upload',
formData,
{
headers: {
'Content-Type': 'multipart/form-data'
}
}
).then(function (response) {
self.$toasted.show(response.data.message, {type: "success"});
})
.catch(function (e) {
self.$toasted.show(e.response.data.message, {type: "error"});
});
}
}
}
</script>
Apparently I had to use v-show instead of v-if, because v-if would be 'lazy' and will not render my error-div when the errors var gets filled.
It's working now, but not 100% sure if this is the best way, as I found another tutorial where v-if is used for form validation.(https://medium.com/#mscherrenberg/laravel-5-6-vue-js-simple-form-submission-using-components-92b6d5fd4434)
I was getting the same error, this is how I solved the problem,
<div v-if="errors.field1.length > 0 ? true : false"> // true or false
If you fix the code like this it will work
The reason might because the way you reassign object is not reactive, which not trigger v-if to re-calculate
this.errors.selectedSupplier = 'Supplier required';
this.errors.file = 'File required';
If you still want to use v-if , try change to this approach
this.errors = {...this.errors, selectedSupplier: 'Supplier required' }
this.errors = {...this.errors, file: 'File required' }
The way I handle my errors with VueJS is through lists and their length attribute.
I have an errors object in my data that looks like this:
errors: {
field1: [],
field2: [],
}
Then, when I submit the form, I will:
Empty all the lists for the errors (ie clearing the previous errors)
.push() new errors in the right lists (and .push() makes the Vue reactive)
Finally, in my form, my respective errors divs are displayed based on the length of the list:
<div class="error" v-if="errors.field1.length > 0">
use a v-for to display all the errors from the list
</div>
Hope it helps

Vue parent component not re-rendering when computed properties are changing

I have a User-Select component that wraps a v-select. This components job is to fetch list of users as user types and allow the user to select one or more users.
Here is the code for this component.
<template>
<div>
<v-select
id="recipients"
name="recipients"
:options="options"
label="name"
multiple
#search="onSearch"
v-model="selectedVal"
/>
</div>
</template>
<script>
import vSelect from 'vue-select'
import axios from 'axios'
import _ from 'lodash'
export default {
name: 'UserSelect',
components: {
'v-select': vSelect
},
props: {
value: {
type: Array
}
},
data() {
return {
options: []
}
},
computed: {
selectedVal: {
get() {
return this.value
},
set(val) {
//this.value = val
this.$emit('input', val)
}
}
},
methods: {
onSearch(search, loading) {
loading(true)
this.search(loading, search, this)
},
setSelected: function(val) {
this.$emit('input', val)
},
search: _.debounce((loading, search, vm) => {
axios
.post('searchPeople', { search }, { useCredentails: true })
.then(res => {
vm.options = res.data
loading(false)
})
}, 350)
}
}
</script>
<style lang="scss" scoped>
#import url('https://unpkg.com/vue-select#latest/dist/vue-select.css');
</style>
As you can see I have a v-model linked to a computed property , which emits input event. Also my property name is value. Hence, I expect the parent component,that is using this UserEvent component to be able to v-model.
In the parent component , i have a computed property to which I have v-modelled the selected value. Here is the code.
<template>
<div>
<b-modal id="editMessage" :title="title" :static="true">
<form id="newMessageForm" class="row">
<div class="col-md-12">
<div class="form-group row">
<label for="to" class="col-sm-3 col-form-label">To:</label>
<user-select
class="col-sm-7"
style="padding-left: 0px; padding-right: 0px"
v-model="editedMessage.recipients"
/>
</div>
<div class="form-group row">
<label for="subject" class="col-sm-3 col-form-label"
>Subject:</label
>
<input
id="subject"
name="subject"
type="text"
class="form-control col-sm-7"
:value="editedMessage.messageSubject"
/>
</div>
<div class="form-group row">
<label for="date" class="col-sm-3 col-form-label"
>Schedule for later :
</label>
<input
type="checkbox"
class="form-control col-sm-7"
v-model="scheduleForLater"
id="scheduleCheckBox"
/>
</div>
<div class="form-group row" v-if="scheduleForLater">
<label for="date" class="col-sm-3 col-form-label"
>Scheduled Date:</label
>
<datetime
v-model="editedMessage.sentDate"
type="datetime"
class="col-sm-17"
input-class="form-control col-sm-15"
input-id="date"
/>
</div>
<div class="form-group row">
<label for="body" class="col-sm-3 col-form-label">Message:</label>
<textarea
id="body"
name="body"
type="text"
rows="10"
class="form-control col-sm-7"
:value="editedMessage.messageBody"
></textarea>
</div>
</div>
</form>
<template v-slot:modal-footer="{ hide }">
<!-- Emulate built in modal footer ok and cancel button actions -->
<b-button size="sm" variant="light" #click="hide()">
Cancel
</b-button>
<b-button
size="sm"
variant="secondary"
#click="
sendMessage(true)
hide()
"
>
Save Draft
</b-button>
<b-button
size="sm"
variant="primary"
#click="
sendMessage(false)
hide()
"
>
Send
</b-button>
</template>
</b-modal>
</div>
</template>
<script>
import { mapState } from 'vuex'
import UserSelect from '#/components/UserSelect.vue'
import axios from 'axios'
export default {
name: 'NewMessage',
components: {
'user-select': UserSelect
},
data() {
return {
options: [],
scheduleForLater: false
}
},
mounted() {},
computed: {
...mapState({
openMessage: state => state.message.openMessage,
messageAction: state => state.message.messageAction
}),
editedMessage: {
get() {
if (this.messageAction === 'new') {
return this.newMessage()
} else if (this.messageAction === 'reply') {
let openMessageClone = Object.assign({}, this.openMessage)
// make sender as the recipient.
return Object.assign(openMessageClone, {
messageSubject: 'RE: ' + this.openMessage.messageSubject,
recipients: [
{
name: this.openMessage.sender.name,
id: this.openMessage.sender.id
}
]
})
} else {
let openMessageClone = Object.assign({}, this.openMessage)
return Object.assign(openMessageClone, {
messageSubject: 'FW: ' + this.openMessage.messageSubject
})
}
},
set(val) {
this.$emit('input', val)
}
},
title() {
if (this.messageAction === 'new') {
return 'New'
} else if (this.messageAction === 'reply') {
return 'Reply'
} else {
return 'Forward'
}
}
},
methods: {
newMessage() {
return {
messageBody: '',
messageSubject: '',
recipients: [],
sender: {}
}
},
sendMessage(saveOrUpdateDraft) {
var url = ''
var message = {
recipients: this.editedMessage.recipients.map(x => x.id),
subject: this.editedMessage.messageSubject,
body: this.editedMessage.messageBody,
sentDate: this.scheduleForLater ? this.editedMessage.sentDate : null,
id: ['editDraft', 'viewScheduled'].includes(this.messageAction)
? this.editedMessage.messageId
: null
}
// id indiciates message id of draft message if one was opened.
if (saveOrUpdateDraft) {
// if no changes have been made to an opened draft, or the draft is completely empty for a new or existing draft , just go back.
if (message.id) {
url = `updateDraft`
} else {
url = 'saveNewDraft'
}
} else {
if (message.id) {
url = `sendSavedMessage`
} else {
url = 'sendMessage'
}
}
axios
.post(`eventdirect/${url}`, message, {
withCredentials: true
})
.then(response => {
if (url.includes('Draft') && this.messageAction === 'viewScheduled') {
this.$store.dispatch('sent/moveToDraft', response.data)
} else if (url.includes('Draft')) {
this.$store.dispatch('drafts/updateDraft', response.data)
} else {
// else part is sending a message or scheduling a draft.
if (this.messageAction === 'editDraft') {
this.$store.dispatch('drafts/deleteDraft', response.data)
}
// if we are sending an existing scheduled messsage , just update the sent vuex store , so that the message moves from scheduled to sent bucket.
if (this.messageAction === 'viewScheduled') {
this.$store.dispatch('sent/updateMessage', response.data)
} else {
this.$store.dispatch('sent/addSentItem', response.data)
}
}
})
.catch(() => {
// TODO , add a qtip here to notify user , this message should be sent later.
// messages in draft store with target , should be posted to the target
this.$store.dispatch('drafts/updateDraft', {
...message,
target: url
})
})
}
}
}
</script>
Now i can see in the vue dev tools the computed values in this NewMessage component gets changed. However this component does not re-render and the selected values are not passed down to UserSelect component until I toggle , schedule for later checkbox , that causes the components data to change and this triggers the Vue component to suddenly start showing the selected values.
Whats going on here. There is something about Vue's reactivity that I am not able to understand here.
Thanks in anticipation. You can try it here, or click on the edit sandbox button above to view and edit the code.
To try it, click on the Vue Message link , then hit reply , then type ‘Test’ and then select ‘Test User’ from the drop down. The selected user wont show until you click the checkbox - Schedule later.
PS: Within the component , UserSelect, In the setter of the computed property selectedVal , if I manually set the value of property value (by simply uncommenting line 39 - which is commented right now) , everything works fine. However , I get a warning that property should not be directly set. Since I am emitting the input event , parent component should change its v-model and re-render , thereby causing child component to re-render. Please correct me , if my understanding is wrong. Problem once again is that the parent component’s v-model changes , but it doesn’t re-render.