Get event click on backdrop using modal Bootstrap-vue - javascript

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.

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;
}
}

How to 2-way bind props with local data of a component in Vue 3?

Can anyone tell me how I can bind the prop of a component to a data property of its own? For example, consider I have a component called ModalComponent
<template> // ModalComponent.vue
<div v-if="showModal">
...
<button #click="showModal = !showModal">close the modal internally</button>
</div>
</template>
<script>
export default {
props: {
show: {
type: Boolean,
default: false
},
},
data() {
return {
showModal: false,
}
}
}
</script>
<style>
</style>
Now consider I am using this modal as a reusable component from inside a parent component using an external button to open up the pop up for the modal.
<template>
<button #click="showChild = !showChild">click to open modal</button>
<ModalComponent :show="showChild" />
</template>
<script>
export default {
components: {ModalComponent},
data() {
return {
showChild: false,
}
}
}
</script>
<style>
</style>
How do I make it so that every time the parent clicks on the button, the modal pops up by binding the local showModal to the prop show ? And when the modal is closed internally by the local close button, if I click on the parent button again it will re-pop up? (I am using Vue 3, just in case)
Any help would be appreciated. Thanks.
The perfect solution for this case is using v-model instead of passing a prop :
in child component define modelValue as prop and emit its value to the parent using $emit function:
<template> // ModalComponent.vue
<div v-if="modelValue">
...
<button #click="$emit('update:modelValue',!modelValue)">close the modal internally</button>
</div>
</template>
<script>
export default {
props: {
modelValue: {
type: Boolean,
default: false
},
},
emits: ['update:modelValue'],
}
</script>
in parent component just use v-model to bind the value :
<template>
<button #click="showChild = !showChild">click to open modal</button>
<ModalComponent v-model="showChild" />
</template>
<script>
export default {
components: {ModalComponent},
data() {
return {
showChild: false,
}
}
}
</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.

VueJS: v-on:click not working on clicking the <button>

I have a Vue app configured by NuxtJS.
I have the following template and worker methods that should be called upon clicking the button. But they are not being called.
<template>
<button class="btn google-login" id="google-login" #click.native="googleLogin">
<img src="~assets/google-icon.svg" alt />
Login with Google
</button>
</template>
<script>
import firebase from "firebase";
export default {
data() {
return {
showPassword: false,
checkbox1: false
};
},
mounted: function() {
console.log(firebase.SDK_VERSION);
},
methods: {
googleLogin: function(event) {
console.log('Reached inside the function');
let googleProvider = new firebase.auth.GoogleAuthProvider();
googleProvider.addScope(
"https://www.googleapis.com/auth/contacts.readonly"
);
firebase.auth().useDeviceLanguage();
console.log(googleProvider);
}
}
};
</script>
I have the method inside the methods object. I have tried multiple solutions v-on:click, #click, #click.prevent but none seem to be working
.native event modifier is used with elements when you are trying listen any event happening in the child Component from the root Component.
For example you have a component button-counter, and your parent component need to listen to the click event happening in the button-counter component.
In your case you just neeed to trigger click event using #click="googleLogin"
Official Documentation
Read More
Sample implementation
new Vue({
el: "#app",
name: "MyApp",
components: {
'button-counter': {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
},
},
methods: {
googleLogin: function (event) {
console.log('Reached inside the function');
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.4/vue.js"></script>
<div id="app">
<button class="btn google-login" id="google-login" #click="googleLogin">
Login with Google
</button>
<button-counter #click.native="googleLogin" />
</div>
You need to trigger the click event this way.
#click="googleLogin"

Prop value isn't updating in child component Vue.js

I'm adding a way for the parent App.vue tell all child components to close every modal you have open and for any given child to tell the parent to hide the overlay if someone clicks on the main app div. So for example if the services page has a modal open to view some details, an overlay will be active that is set in the parent App.vue component, and if I click anywhere outside of the modal, the modal will close and the child will tell the parent to close the overlay. I have it setup this way so that it's something globally available and not isolated to a single component.
<template>
<div id="app" v-cloak :class="{'overlay-layer' : overlay.active}" v-on:click="close($event)">
<Header/>
<transition>
<router-view v-on:overlay="overlay.active = $event" :closeAllModals="overlay.close"/>
</transition>
<Footer/>
</div>
</template>
<script>
import Header from '#/components/header/Header.vue';
import Footer from '#/components/footer/Footer.vue';
export default {
components: {
Header,
Footer
},
props: {
closeAllModals: Boolean
},
data: function() {
return {
overlay: { active: false, close: false }
};
},
methods: {
close(e) {
if (this.overlay.active && e.toElement.id === 'app') {
this.overlay = {
active: false,
close: true
}
}
}
}
};
</script>
The problem is that this only executes once, so for example in the services page I have:
import Header from '#/components/header/Header.vue';
import Footer from '#/components/footer/Footer.vue';
export default {
name: 'services',
components: {
Header,
Footer
},
props: {
closeAllModals: Boolean
},
data: function() {
return {
overlay: false,
activeMember: Object,
};
},
methods: {
setActiveMember(member, open) {
this.activeMember = member;
if (open) {
this.activeMember.active = true;
this.overlay = true;
} else {
this.activeMember.active = false;
this.overlay = false;
}
this.$emit('overlay', this.overlay);
this.$forceUpdate();
},
watch: {
closeAllModals: function() {
this.activeMember.active = false;
this.overlay = false;
this.$forceUpdate();
console.log('runs once');
}
}
};
So this works, but only works the first time. The prop only sends the updated value to the child only once. I've tried watching the prop in the child and using forceUpdate too but it isn't working. How do I make this run every single time?
A global event bus could help when components have to talk to each other across different parent/child levels. Below is a good write up on that:
https://alligator.io/vuejs/global-event-bus
Note: but be sure to use it only when necessary and remove the listeners in beforeDestroy life cycle of the component

Categories