Can't open Vuetify dialog programmatically in setTimeout callback - javascript

By default, the displaying of Vuetify dialog is controlled by a button toggling the value of dialog Boolean variable.
I was assuming that programmatically changing the value of this variable would allow to show or hide the dialog, but it doesn't. Why not?
Here's my code:
<template>
<div>
<v-dialog v-model="dialog">
<v-card>
Dialog content
</v-card>
</v-dialog>
</div>
</template>
<script>
export default {
data() {
return {
dialog: false
}
},
mounted() {
console.log(this.dialog);
setTimeout(function() {
this.dialog = true;
console.log(this.dialog);
}, 2000);
}
}
</script>
Console shows false on page load, then true after 2 seconds. But dialog still doesn't show up...

You should use arrow function ()=> as setTimeout callback :
mounted() {
console.log(this.dialog);
setTimeout(()=> {
this.dialog = true;
console.log(this.dialog);
}, 2000);
}
See the Pen
Vuetify Dialog example by boussadjra (#boussadjra)
on CodePen.

you're having some trouble calling the variable inside the function of the setTimeout.
try this:
<template>
<div>
<v-dialog v-model="dialog">
<v-card>
Dialog content
</v-card>
</v-dialog>
</div>
</template>
<script>
export default {
data() {
return {
dialog: false
}
},
mounted() {
that = this
console.log(this.dialog);
setTimeout(function() {
that.dialog = true;
console.log(that.dialog);
}, 2000);
}
}
</script>
try to read this answer from a related issue with calling this inside anonymous functions

Related

Call function on child component after emit

I'm trying to create a custom button component with a spinner, it works fine - disables the button and shows a spinner when clicked but I'm having trouble resetting the button's state when whatever is "loading" is finished.
My current code is as follows:
Parent.vue
<template>
<custom-button #button-click="requestAPI" :disabled="canRequestAPI">
Request API
</custom-button>
</template>
<script>
methods: {
async requestAPI(){
// Request API here
}
</script>
CustomButton.vue
<template>
<button :disabled="disabled || loading" #click="processClick" :loading="loading">
<img src="/loader.svg" />
<span class="buttonContent">
<slot />
</span>
</button>
</template>
<script>
props: ["disabled"],
data() {
return: {
loading: false,
}
},
methods: {
processClick(){
this.$emit('button-click');
this.loading = true;
}
}
</script>
The spinner shows and the API is requested but I can't work out how to stop the spinner in a good way. I could create a prop to watch and set loading to false but I'd like to be able to use this custom button several without having to add lots of different variables.
Ideally i'd be able to do something like:
Parent.vue
<script>
methods: {
async requestAPI(e){
// Request API here
e.stopLoading();
}
</script>
CustomButton.vue
<script>
methods: {
stopLoading(){
this.loading = false;
}
}
</script>
Worked it out, Just emitted the stopLoading function to the parent and called that.
Parent.vue
<script>
methods: {
async requestAPI(stopLoading){
// Request API here
stopLoading();
}
}
</script>
Child.vue
methods: {
processClick(){
this.$emit('button-click', () => this.stopLoading());
this.loading = true;
},
stopLoading(){
this.loading = false;
}
}

Vuetify v-dialog is mounted multiple times after use and is not within a loop

I have two dialogs; SignIn & SignUp. The dialog use :value="dialog" instead of v-model:dialog since it the dialog is a computed data. The dialog is taken from a vuex module (popup). The dialogs are done without activator (provided by Vuetify).
If "Sign Up" button is clicked within the PopupSignIn component, it will close itself and open PopupSignUp by calling togglePopupAccountSignInSignUp and vice versa. The Dialogs can toggle fine but ONCE I fill in a form to sign in or sign up and executing the action. It signs me in and Vue gave an error (1st). Then another (2nd) error when logging out and opening the Sign In dialog again.
Errors
1st Error
vue.runtime.esm.js?2b0e:619 [Vue warn]: Error in nextTick: "TypeError: Cannot read property 'focus' of undefined"
found in
---> <VDialog>
<PopupSignIn> at src/components/popup/SignIn.vue
<VApp>
<App> at src/App.vue
<Root>
2nd Error
Uncaught RangeError: Maximum call stack size exceeded.
What I did
Add :retain-focus="false" to both Dialogs. It removes the error but
the Dialogs are still mounted multiple times
Replace v-if="!this.user" with v-show="!this.user"; still mounting multiple times
Use setTimeout before closing the Dialogs followed with router.push
Reload the page every time a user logout. Although this work, I would need to always refresh
everytime a Dialog is used
location.reload();
return false;
App.vue
<!-- There is no loop going on within the header tag -->
<header>
...
<PopupSignIn v-if="!this.user" />
<PopupSignUp v-if="!this.user" />
...
</header>
popup.js
export const namespaced = true;
export const state = {
account: {
signIn: false,
signUp: false
}
};
export const mutations = {
SET_POPUP_ACCOUNT_SIGN_IN(state, bool) {
state.account.signIn = bool;
},
SET_POPUP_ACCOUNT_SIGN_UP(state, bool) {
state.account.signUp = bool;
}
};
export const actions = {
updatePopupAccountSignIn({ commit }, bool) {
commit("SET_POPUP_ACCOUNT_SIGN_IN", bool);
},
updatePopupAccountSignUp({ commit }, bool) {
commit("SET_POPUP_ACCOUNT_SIGN_UP", bool);
},
togglePopupAccountSignInSignUp({ commit, state }) {
commit("SET_POPUP_ACCOUNT_SIGN_IN", !state.account.signIn);
commit("SET_POPUP_ACCOUNT_SIGN_UP", !state.account.signIn);
}
};
SignIn.vue (PopupSignIn)
<template>
<div>
<v-btn #click.stop="updatePopupAccountSignIn(true)">
<v-icon>
mdi-account
</v-icon>
Sign In
</v-btn>
<v-dialog
:value="dialog"
:retain-focus="false"
#click:outside="updatePopupAccountSignIn(false)"
>
<v-card>
<v-card-title>
<div>Sign In</div>
</v-card-title>
<v-card-text>
<v-form
ref="form"
v-model="valid"
lazy-validation
#submit.prevent="login"
>
<!-- Rest of the form -->
<v-btn
type="submit"
:loading="loading"
:disabled="loading"
>Sign In</v-btn
>
</v-form>
<div>
<p>Don't have an account?</p>
<v-btn
#click.stop="togglePopupAccountSignInSignUp"
>Sign Up</v-btn
>
</div>
</v-card-text>
</v-card>
</v-dialog>
</div>
</template>
<script>
export default {
computed: {
...mapState("popup", {
dialog: state => state.account.signIn
})
},
methods: {
...mapActions("account", ["signIn"]),
...mapActions("popup", [
"updatePopupAccountSignIn",
"updatePopupAccountSignUp",
"togglePopupAccountSignInSignUp"
]),
async login() {
try {
this.loading = true;
await this.signIn({
email: this.email,
password: this.password.value
});
...
setTimeout(() => {
this.updatePopupAccountSignIn(false);
this.updatePopupAccountSignUp(false);
this.$router.push("/your/profile");
}, 2000);
} catch (error) {
...
}
}
}
};
</script>
The SignUp component is roughly the same with the SignIn component flow, the only difference is that it's for registering.
I'm not sure what's causing the Dialogs to be mounted multiple times. Any help to solve this is greatly appreciated.
I've tried a different approach to get the expected result using Vue .sync Modifier and removing the vuex method. More can be read from Jithil Jishad's blog.
App.vue
<template>
<header>
...
<PopupSignIn
v-if="!user"
:dialog.sync="dialog.signIn"
:dialogSignUp.sync="dialog.signUp"
/>
<PopupSignUp
v-if="!user"
:dialog.sync="dialog.signUp"
:dialogSignIn.sync="dialog.signIn"
/>
...
</header>
</template>
<script>
export default {
...
data() {
return {
dialog: {
signIn: false,
signUp: false
}
};
},
...
};
</script>
SignIn.vue
<template>
<v-dialog
v-model="dialog"
#click:outside.stop="$emit('update:dialog', false)"
>
<v-card>
<v-card-text>
<!-- FORM -->
<p>Don't have an account?</p>
<v-btn #click.stop="toggleDialog">Sign Up</v-btn>
</v-card-text>
</v-card>
</v-dialog>
</template>
<script>
export default {
props: {
dialog: Boolean,
dialogSignUp: Boolean
},
beforeDestroy() {
this.$emit("update:dialog", false);
},
methods: {
toggleDialog() {
this.$emit("update:dialog", false);
this.$emit("update:dialogSignUp", true);
}
}
};
</script>

Properly using click:outside with vuetify dialog

I have a v-dialog that I use to pop up a date picker when needed. To show it, I bind a value with v-modle. I use the click:outside events to trigger to function that is supposed to close it once it's clicked outside so I can trigger it again (if I click outside, the dialog is dismissed, but the value stays 'true', so I can't show it more than once). There must be something I'm doing wrong.
Here's my dialog :
<template>
<v-dialog
v-model="value"
#click:outside="closeDialog"
>
<v-date-picker
v-model="date"
/>
<v-divider/>
<div>fermer</div>
</v-dialog>
</template>
<script>
export default {
name: 'DatePickerDialog',
props: ['value'],
data() {
return {
colors,
date: null,
}
},
methods: {
closeDialog() {
this.value = false;
}
}
}
</script>
And what calls it is as simple as this :
<template>
<div>
<v-btn #click="inflateDatePicker">inflate date picker</v-btn>
<date-picker-dialog v-model="showDatePicker"/>
</div>
</template>
<script>
import DatePickerDialog from '../../../../views/components/DatePickerDialog';
export default{
name: "SimpleTest",
components: {
DatePickerDialog
},
data() {
return {
showDatePicker: false,
};
},
methods:{
inflateDatePicker() {
this.showDatePicker = true;
},
},
}
</script>
So I can inflate it with no problem. Then though I'm not able to go inside closeDialog() (verified by debugging and trying to log stuff). So what is happening that makes it so I'm not able to enter the function? Documentation doesn't specify if you need to use it differently from regular #click events
The problem was in my closeDialog function. this.value = false did not notify the parent component of the value change. So I had to change it to this in order for it to work properly :
closeDialog() {
this.$emit('input', false);
},
With this is works perfectly fine.

Get event click on backdrop using modal Bootstrap-vue

Im using Vue2 and Boostrap-vue to make an modal component here's the code
I passed the following props #backdrop="handleBackdrop"
<template>
<b-modal
v-model="showModal"
id="modal"
#backdrop="handleBackdrop"
#ok="handleBackdrop"
>
...
</template>
<script>
data() {
return {
showModal: false
}
},
methods: {
show() {
this.showModal = true;
},
hide() {
this.showModal = false;
},
handleBackdrop(ev) {
ev.preventDefault();
console.log("Click backdrop");
}
}
</script>
i'm using this #ok to test the function handleBackdrop and it works but i cannot active this click on backdrop
There's no backdrop event on the modal, that's why you aren't seeing anything happening.
What you can do instead is watch the hide event which contains a trigger property.
This trigger property will contain what triggered the hide event, including backdrop if that was clicked.
https://bootstrap-vue.js.org/docs/components/modal/#prevent-closing
<template>
<b-modal id="modal" #hide="onHide"></b-modal>
</template>
<script>
new Vue({
el: '#app',
methods: {
onHide(evt) {
if(evt.trigger === "backdrop"){
evt.preventDefault();
this.handleBackdrop();
}
},
handleBackdrop() {
console.log('Backdrop clicked')
}
}
})
</script>
If you simply want to prevent the modal closing if the backdrop is clicked you can add the no-close-on-backdrop to the modal to prevent this behavior.

Call function if switch gets toggled in VueJS/VuetifyJS

I'm using VuetifyJS for VueJS and I need to call a function if the switch gets toggled. How to do that?
Template:
<v-container fluid>
<v-switch :label="`Switch 1: ${switch1.toString()}`" v-model="switch1"></v-switch>
</v-container>
</template>
Script:
<script>
export default {
data () {
return {
switch1: false
}
}
}
</script>
Found another better solution to use #change prop, Which calls the method every time the switch is toggled.
<v-switch
:input-value="true"
#change="changeState(item.id)"
></v-switch>
<v-switch
:input-value="true"
#change="changeState(item.id)"
></v-switch>
Here's script,
methods:{
changeState(id) {
console.log("Switch "+id);
// Do stuff
}
}
You can set up a watcher on switch1 data property as follows:
<script>
export default {
data () {
return {
switch1: false
}
},
watch: {
switch1(newValue){
//called whenever switch1 changes
console.log(newValue);
}
}
}
</script>
Since the v-model of v-switch is bound to switch1 , whenever the switch is toggled on/off the watch handler of switch1 gets called with the changed newValue
Here is a codepen

Categories