Magnific popup calls 2 confirmation when closed - magnific-popup

I used magnific popup for form modal. I want confirmation when modal was closed by user. But, the problem is that 2 confirmations are being displayed. I want only one. Here is my code
<a href="#registration" class="popup-modal">
<span>Register</span>
</a>
<div id="registration" class="white-popup-block mfp-hide">
<button title="Close" type="button" class="mfp-close"><i class="icon-cross" aria-hidden="true"></i></button>
<div class="mfp-text">
<!-- My Form Here -->
</div>
</div>
$('.popup-modal').magnificPopup({
type: 'inline',
preloader: false,
modal: true,
callbacks: {
open: function() {
$.magnificPopup.instance.close = function () {
if (!confirm("Are you sure?")) {
return;
}
$.magnificPopup.proto.close.call(this);
};
}
}
});
What is the possible problem of my code. Why did the popup when closed shows 2 confirmation dialog? Please help. Thanks!

Related

How to show a hidden div by clicking on another div (button) in Vue?

I am trying to show the loading div that's normally hidden after clicking a confirm button, even after changing the hidden value on click.
here is my code:
<div class="form-btns">
<button type="button" class="btns full-width" v-on:click="submitPhoneNumber(); isHidden = false">
{{t('confirmNumber')}}
</button>
<loader title="" v-if="!isHidden"></loader>
<button type="button" class="btns hyperlink">
{{t('accountRecovery')}}
</button>
</div>
data() {
return {
isHidden: true,
};
Your code is a little funky. Get rid of how you're trying to set the value directly in the template HTML and set it in your function in the methods. So your new code would be:
<div class="form-btns">
<button type="button" class="btns full-width" v-on:click="submitPhoneNumber()">
v {{t('confirmNumber')}}
</button>
<loader title="" v-if="!isHidden"></loader>
<button type="button" class="btns hyperlink">
{{t('accountRecovery')}}
</button>
</div>
Then in your methods just toggle the value like this:
data() {
return {
isHidden: true,
}
},
methods: {
submitPhoneNumber() {
this.isHidden = false; // I would probably rename this to isLoading and invert the logic
// Other stuff...
}
}
You should change the isHidden value inside the submitPhoneNumber, either as below, or with a dedicated function that you call in the first one.
let app = new Vue({
el: '#app',
data: {
isHidden: true
},
methods: {
submitPhoneNumber() {
this.isHidden = false;
// Simulate an API response after 3s to hide the loader
setTimeout(() => { this.isHidden = true; }, 3000);
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button type="button" #click="submitPhoneNumber()">
Submit button
</button>
<p v-if="!isHidden">Loading...</p>
</div>

Init Vue Awesome swiper on product card hover

ideally I need vue awesome swiper to init on product card hover, so until the user hovers, catalog loads only one img per product instead of multiple.
HTML
<div
#mouseover="handleMouseOver"
#mouseleave="handleMouseLeave"
/>
<div
v-if="media.length > 1"
ref="swiper"
v-swiper:mySwiper="swiperOption"
>
<div class="swiper-wrapper">
<div
v-for="image in images"
:key="image.id"
class="swiper-slide image__wrapper"
>
<img
:src="image.attributes.src"
:width="imgWidth"
:height="imgHeight"
:alt="imgAlt"
>
</div>
</div>
</div>
</div>
Component
data: () => ({
swiperOption: {
loop: true,
slidesPerView: 1,
centeredSlides: true,
spaceBetween: 30,
},
}),
methods: {
slideStart() {
if (this.$refs.swiper) {
this.mySwiper.activeIndex = 2;
this.mySwiper.autoplay.start();
}
},
slideStop() {
if (this.$refs.swiper) {
this.mySwiper.autoplay.stop();
this.mySwiper.activeIndex = 1;
}
},
handleMouseOver() {
this.isHovered = true;
this.slideStart();
},
handleMouseLeave() {
this.isHovered = false;
this.slideStop();
},
},
What I've tried and what problems encountered:
At first, I've added isHovered condition to v-if and used element in v-else, however after hover swiper refuses to autoplay (but reacts on activeIndex change)
After that I've tried adding init:false to swiperOption and this.$mySwiper.init() on hover, but it crashes whenever I'm trying to leave the page:
Would appreciate any ideas.
Solved by creating a parent div, and moving 'v-if' logic with hover condition there.

(Vue.js) Scrolling in the modal will also scroll the back of the modal

Clicking on the image brings up a long, scrolling modal. The problem is that if you scrolling in the modal will also scroll the back of the modal. How do you solve it?
Modal is a component. Here is my code:
Carousel.vue
<template>
<div>
<div v-for="(item, index) in photos" :key="index">
<div #click="imgClick(item)" style="cursor:pointer;">
<img :src="item.thumbnail" />
</div>
<Modal v-if='item.show' #close="item.show = false">
<div slot='body'>
<img :src="item.thumbnail" :class="`img-index--${index}`"/>
</div>
</Modal>
</div>
</div>
</template>
<script>
import Modal from './Modal.vue'
export default {
props: {
items: { type: Array, default: () => [] }
},
data() {
return {
photos: {}
}
},
created() {
this.photos = this.items.map(item => {
return { ...item, show: false }
})
},
methods: {
imgClick(item) {
item.show = true
}
},
components: {
Modal: Modal
}
}
</script>
Most modal packages solve this by applying a class to the <body> tag when a modal is opened. For example, <body class="modal-open">. Then in your application's CSS, you can add this rule:
body.modal-open {
overflow: hidden;
}
This will make it so that the page behind the modal is no longer scrollable.
Whichever modal package you are using likely fires events when the modal is opened or closed. You can apply this class to the <body> tag in the open event handler, and remove the class from the <body> tag in the close event handler.
UPDATE
Based on the code you added, here's how you can toggle the modal-open class on the <body> tag:
...
<div #click="showModal(item)" style="cursor:pointer;">
<img :src="item.thumbnail" />
</div>
<Modal v-if='item.show' #close="hideModal(item)">
<div slot='body'>
<img :src="item.thumbnail" :class="`img-index--${index}`"/>
</div>
</Modal>
...
{
...
methods: {
showModal(item) {
item.show = true
document.body.classList.add("modal-open");
},
hideModal(item) {
item.show = false;
document.body.classList.remove("modal-open");
}
},
...
}
See this jsFiddle for reference.
I can't see your specific code running, but you can generally fix this by setting the body of your page to "overflow:hidden" when you open your modal. To make it more elegant, check to see if the page has scrollbars and add some margin to the right side to prevent content shift. I've used this scrollbar detection in the past, but I'd strip it down to the bare minimum if I used it today... I no longer care about old IE.
You can temporarily disable events that cause scrolling.
How to disable scrolling temporarily?
Some solutions just disable scrolling by hiding the scrollbar (overflow: hidden; on the body element), but this solution has the benefit of the background content not shifting over if the scrollbar is styled to affect layout, which most browsers do if you're using a non-touch mouse.

Ignore certain links in the html magnific-popup

I need Magnific-popup to bind to only certain links in the HTML. This is so when one linked is clicked it will link out to another website. If the other link is clicked it will enlarge the image.
Right now the link the being clicked that is not wrapped in an image the popup is initialized.
This is the HTML
<div class="field__item">
<div class="mfp-field mfp-separate-item">
<a href="path-to-origin-image">
<!-- This link is being added to the popup and works correctly-->
<img class="mfp-thumbnail" src="img-url" />
</a>
</div>
<div class="field--name-field-url">
<!-- This link is being added to the popup and should be ignored -->
Google
</div>
</div>
This is the jquery
$(context).find('.field--name-field-gallery-items').once('mfp-processed').each( function() {
$(this).magnificPopup({
delegate: 'a',
type: 'image',
gallery: {
enabled: true
},
image: {
titleSrc: function (item) {
return item.img.attr('alt') || '';
}
}
});
});
$(context).find('.field--name-field-gallery-items .field--name-field-url a').each().unbind('click');
How do I stop magnificPopup from binding to the second link?
The solution is the following:
$(context).find('.field--name-field-gallery-items').once('mfp-processed').each( function() {
$(this).magnificPopup({
delegate: '.mfp-field a',
type: 'image',
gallery: {
enabled: true
},
image: {
titleSrc: function (item) {
return item.img.attr('alt') || '';
}
}
});
});
I should have read the api properly

Using Vee-validate to disable button until form is filled out correctly

I want to disable my submit button until my form is filled out correctly, this is what I have so far:
<form>
<input type="text" class="form-control" v-validate="'required|email'" name="email" placeholder="Email" v-model="userCreate.userPrincipalName" />
<span v-show="errors.has('email')">{{ errors.first('email') }}</span>
<button v-if="errors.any()" disabled="disabled" class="btn btn-primary" v-on:click="sendInvite();" data-dismiss="modal" type="submit">Send Invite</button>
<button v-else="errors.any()" class="btn btn-primary" v-on:click="sendInvite();" data-dismiss="modal" type="submit">Send Invite</button>
</form>
The above only prints an error message and disables my submit button after I've started inputting a value. I need it to be disabled from the start, before I start interacting with the input, so that I cannot send an empty string.
Another question is if there is a better way than using v-ifto do this?
EDIT:
userCreate: {
customerId: null,
userPrincipalName: '',
name: 'unknown',
isAdmin: false,
isGlobalAdmin: false,
parkIds: []
}
Probably simpliest way is to use ValidationObserver slot for a form. Like this:
<ValidationObserver v-slot="{ invalid }">
<form #submit.prevent="submit">
<InputWithValidation rules="required" v-model="first" :error-messages="errors" />
<InputWithValidation rules="required" v-model="second" :error-messages="errors" />
<v-btn :disabled="invalid">Submit</v-btn>
</form>
</ValidationObserver>
More info - Validation Observer
Setting up the button to be :disabled:"errors.any()" disables the button after validation. However, when the component first loads it will still be enabled.
Running this.$validator.validate() in the mounted() method, as #im_tsm suggests, causes the form to validate on startup and immediately show the error messages. That solution will cause the form to look pretty ugly. Also, the Object.keys(this.fields).some(key => this.fields[key].invalid); syntax is super ugly.
Instead, run the validator when the button is clicked, get the validity in the promise, and then use it in a conditional. With this solution, the form looks clean on startup but if they click the button it will show the errors and disable the button.
<button :disabled="errors.any()" v-on:click="sendInvite();">
Send Invite
</button>
sendInvite() {
this.$validator.validate().then(valid=> {
if (valid) {
...
}
})
}
Validator API
One way to disable a button until all the values you need are filled, is to use a computed property that will return bool if all values are assigned or not
Example:
Create a computed property like this:
computed: {
isComplete () {
return this.username && this.password && this.email;
}
}
And bind it to the html disabled attribute as:
<button :disabled='!isComplete'>Send Invite</button
This means, disable the button if !isComplete is true
Also, in your case you don't need two if/else-bound buttons. You can use just one to hide/show it based on if the form is completed or has any errors:
<button :disabled="errors.any() || !isCompleted" class="btn btn-primary" v-on:click="sendInvite();" data-dismiss="modal" type="submit">Send Invite</button>
This button will be disabled until all fields are filled and no errors are found
Another way is to make use of v-validate.initial
<input type="text" class="form-control" v-validate.initial="'required|email'" name="email" placeholder="Email" v-model="userCreate.userPrincipalName" />
This will execute the validation of the email input element after the page is loaded. And makes that your button is disabled before interacting with the input.
To check whether a form is invalid or not we can add a computed property like this:
computed: {
isFormInValid() {
return Object.keys(this.fields).some(key => this.fields[key].invalid);
},
},
Now if you want to start checking immediately before user interaction with any of the fields, you can validate manually inside mounted lifecycle hooks:
mounted() {
this.$validator.validate();
}
or using computed
computed: {
formValidated() {
return Object.keys(this.fields).some(key => this.fields[key].validated) && Object.keys(this.fields).some(key => this.fields[key].valid);
}
}
and use
button :disabled="!formValidated" class="btn btn-primary" v-on:click="sendInvite();" data-dismiss="modal" type="submit">
For the current version 3 (As at the time of writing).
Step 1
Ensure form fields can be watched.
Step 2
Get a reference to the validator instance:
<ValidationObserver ref="validator">.
Step 3
Trigger validation silently whenever the form fields change.
Here's an example:
export default {
data() {
return {
form: {
isValid: false,
fields: {
name: '',
phone: '',
}
}
}
},
watch: {
'form.fields': {
deep: true,
handler: function() {
this.updateFormValidity();
}
}
},
methods: {
async updateFormValidity() {
this.form.isValid = await this.$refs.validator.validate({
silent: true // Validate silently and don't cause observer errors to be updated. We only need true/false. No side effects.
});
},
}
}
<button :disabled="form.isValid">
Submit
</button>
You can add computed properties
...
computed: {
isFormValid () {
return Object.values(this.fields).every(({valid}) => valid)
}
}
...
and it bind to the button:
<button :disabled="!isFormValid" class="btn btn-primary" type="submit">Send Invite</button>
i try this on vee-validate version ^2.0.3