Vue awesome swiper not work properly in vue js modal plugin - vue.js

I'm using vue-js-modal for product quick view and I'm trying to create an image gallery with vue-awesome-swiper but the problem is outside this modal image gallery work fine as I am aspected but inside the modal plugin it shows me an error and does not work properly
I do not find any solution on this topic please help me any body
My code is below
<template>
<modal name="quick-view" width="90%" height="auto" :maxWidth="1000" :maxHeight="600" :adaptive="true">
<div class="modal-content-wrapper">
<button #click="hide" class="modal-close">Close me</button>
<div class="product-details">
<div class="product-carousel">
<!-- swiper1 -->
<swiper class="swiper gallery-top" :options="swiperOptionTop" ref="swiperTop">
<swiper-slide>
<img src="https://res.cloudinary.com/redq-inc/image/upload/c_fit,q_auto:best,w_300/v1589614568/pickbazar/grocery/GreenLimes_jrodle.jpg" alt="">
</swiper-slide>
<swiper-slide>
<img src="https://res.cloudinary.com/redq-inc/image/upload/c_fit,q_auto:best,w_300/v1589614568/pickbazar/grocery/Yellow_Limes_y0lbyo.jpg" alt="">
</swiper-slide>
<swiper-slide>
<img src="https://res.cloudinary.com/redq-inc/image/upload/c_fit,q_auto:best,w_300/v1589614569/pickbazar/grocery/RedCherries_zylnoo.jpg" alt="">
</swiper-slide>
<swiper-slide>
<img src="https://res.cloudinary.com/redq-inc/image/upload/c_fit,q_auto:best,w_300/v1589614568/pickbazar/grocery/CelerySticks_ulljfz.jpg" alt="">
</swiper-slide>
</swiper>
<!-- swiper2 Thumbs -->
<swiper class="swiper gallery-thumbs" :options="swiperOptionThumbs" ref="swiperThumbs">
<swiper-slide>
<img src="https://res.cloudinary.com/redq-inc/image/upload/c_fit,q_auto:best,w_300/v1589614568/pickbazar/grocery/GreenLimes_jrodle.jpg" alt="">
</swiper-slide>
<swiper-slide>
<img src="https://res.cloudinary.com/redq-inc/image/upload/c_fit,q_auto:best,w_300/v1589614568/pickbazar/grocery/Yellow_Limes_y0lbyo.jpg" alt="">
</swiper-slide>
<swiper-slide>
<img src="https://res.cloudinary.com/redq-inc/image/upload/c_fit,q_auto:best,w_300/v1589614569/pickbazar/grocery/RedCherries_zylnoo.jpg" alt="">
</swiper-slide>
<swiper-slide>
<img src="https://res.cloudinary.com/redq-inc/image/upload/c_fit,q_auto:best,w_300/v1589614568/pickbazar/grocery/CelerySticks_ulljfz.jpg" alt="">
</swiper-slide>
</swiper>
</div>
<div class="p-10">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab aspernatur at,
dignissimos dolorum enim ex expedita fuga harum.
</div>
</div>
</div>
</modal>
</template>
<script>
import { Swiper, SwiperSlide } from 'vue-awesome-swiper'
import 'swiper/css/swiper.css'
export default {
name: "Products",
components : {
Swiper, SwiperSlide
},
data() {
return {
swiperOptionTop: {
loop: true,
loopedSlides: 5, // looped slides should be the same
spaceBetween: 10,
},
swiperOptionThumbs: {
loop: true,
loopedSlides: 5, // looped slides should be the same
spaceBetween: 10,
centeredSlides: true,
slidesPerView: 'auto',
touchRatio: 0.2,
slideToClickedSlide: true
}
}
},
mounted() {
this.$nextTick(() => {
const swiperTop = this.$refs.swiperTop.$swiper
const swiperThumbs = this.$refs.swiperThumbs.$swiper
swiperTop.controller.control = swiperThumbs
swiperThumbs.controller.control = swiperTop
})
},
methods : {
show () {
this.$modal.show('quick-view');
},
hide () {
this.$modal.hide('quick-view');
}
}
}
</script>

You have to use an event for ref work use #opened event Emits after modal became visible or started a transition. see this plugin events here
<template>
<modal name="quick-view" width="90%" height="auto" :maxWidth="1000" :maxHeight="600" :adaptive="true"
#opened="opened"
>
............
</modal>
</template>
<script>
export default {
methods: {
opened() {
this.$nextTick(() => {
const swiperTop = this.$refs.swiperTop.$swiper
const swiperThumbs = this.$refs.swiperThumbs.$swiper
swiperTop.controller.control = swiperThumbs
swiperThumbs.controller.control = swiperTop
})
}
}
}
</script>

Related

Conditional CSS proprety set in computed object doesn't evaluate on the browser

I am trying to make the header element's height size to change depending on a criteria. I set the desired heights in a headerHeightClass inside a computed object but it doesn't seem to work.
<template>
<header :class="['w-full', 'text-sm', 'headerHeightClass']">
<div class="fixed top-0 left-0 w-full h-16 bg-white">
<!--Some irrelevant html code -->
</div>
</header>
</template>
<script>
export default {
name: "MainNav",
components: {},
data() {
return {
isLoggedIn: false,
};
},
computed: {
headerHeightClass() {
return {
"h-16": !this.isLoggedIn,
"h-32": this.isLoggedIn,
};
},
},
methods: {
loginUser() {
this.isLoggedIn = true;
},
},
};
</script>
I also tried:
computed: {
headerHeightClass() {
return this.isLoggedIn? "h-32": "h-16",
},
},
The headerHeightClass appears as a simple string in the browser.
You need to write it like this instead of using headerHeightClass as a string (remove ' ')
<template>
<header :class="['w-full', 'text-sm', headerHeightClass]">
<div class="fixed top-0 left-0 w-full h-16 bg-white">
<!--Some irrelevant html code -->
</div>
</header>
</template>
This can also be written without using computed
<template>
<header :class="[
'w-full text-sm',
{
'h-16':!isLoggedIn,
'h-32':isLoggedIn,
}
]">
<div class="fixed top-0 left-0 w-full h-16 bg-white">
<!--Some irrelevant html code -->
</div>
</header>
</template>
You can give a try like this (Just for a demo purpose I used Vue 2 version) :
Vue.component('headerComponent', {
props: ['msg'],
template: '<p>{{ msg }}</p>'
});
var app = new Vue({
el: '#app',
data: {
isLoggedIn: true
}
});
.w-full {
background-color: gray;
width: 100%;
}
.text-sm {
font-size: 12px;
}
.h-16 {
height: 16px;
}
.h-32 {
height: 32px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<header-component
msg="This is a header component"
:class="['w-full text-sm', { 'h-16':!isLoggedIn, 'h-32':isLoggedIn }]">
</header-component>
</div>

Vue don't give #click to a single element when using v-for to create them

I'm trying to make every single element created with the v-for the ability to toggle between 'face' class, but it happens that it toggles everything created with the v-for.
<div class="deckPairs">
<div class="deckBoxOne" v-for="base in cards" :key="base.id" >
<div class="deckBoxBlanc" :class="{face : face}" #click="face = !face">
<img class="deckPairsImage" style="z-index: 3" alt="Vue logo" :src="require('../assets/rightCardSide.png')">
</div>
<div class="deckBoxImg" :class="{face : !face}" #click="face = !face">
<img class="deckPairsImages" style="z-index: 2" :src="require(`../assets/images/${base.url}`)">
</div>
</div>
</div>
Script:
export default {
setup() {
const face = ref(false)
return { face }
},
data(){
return {
face: false,
}
},
methods: {
}
}
Create a component to encapsulate the v-for content.
// new-component.vue
<template>
<div>
<div class="deckBoxBlanc" :class="{face : face}" #click="face = !face">
<img class="deckPairsImage" style="z-index: 3" alt="Vue logo" :src="require('../assets/rightCardSide.png')">
</div>
<div class="deckBoxImg" :class="{face : !face}" #click="face = !face">
<img class="deckPairsImages" style="z-index: 2" :src="require(`../assets/images/${base.url}`)">
</div>
</div>
</template>
export default {
data(){
return {
face: false
}
}
}
Now update v-for to use the new component.
<div class="deckPairs">
<div class="deckBoxOne" v-for="base in cards" >
<new-component :key="base.id"/>
</div>
</div>
You can use base.id instead boolean and v-show directive:
const { ref } = Vue
const app = Vue.createApp({
el: "#demo",
setup() {
const face = ref(false)
const cards = ref([{id: 1, url: "https://picsum.photos/100"}, {id: 2, url: "https://picsum.photos/101"}, {id: 3, url: "https://picsum.photos/102"}])
const rightCardSide = ref("https://picsum.photos/103")
const setFace = (val) => {
face.value = val
}
return { cards, face, rightCardSide, setFace }
},
})
app.mount('#demo')
.deckPairs {
display: flex;
}
.deckBoxOne {
cursor: pointer;
}
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<div id="demo">
<div class="deckPairs">
<div class="deckBoxOne" v-for="base in cards" :key="base.id" >
<div class="deckBoxBlanc" v-show=" face !== base.id" #click="setFace(base.id)">
<img class="deckPairsImage" alt="Vue logo" :src="rightCardSide">
</div>
<div class="deckBoxImg" v-show="face === base.id" #click="setFace(false)">
<img class="deckPairsImages" :src="base.url">
</div>
</div>
</div>
</div>

Add Close Button to Vue with Popper.js Modal Component

UPDATED Here is a codesandbox recreating the issue - Modal Does not hide: https://codesandbox.io/s/vue3-popperjs-modal-64762?file=/src/components/Modal.vue
I am new to Vue and cannot figure out how to Close the modal from the Hide Modal button within the component. I am using Vue.js and Popper.js for a modal/dropdown component.
I have tried emitting, rels and passing a prop but cannot get it to function correctly. I stripped it down to the shell code below.
** Main Component **
<template>
<div>
<Head title="Main" />
<div class="flex items-center">
<div class="flex">
<dropdown :auto-close="false" class="focus:z-10 px-4 hover:bg-gray-100 focus:border-white rounded-l focus:ring md:px-6" placement="auto">
<template #default>
<div class="flex">
<button id="open" class="btn-indigo" type="button">Open Modal</button>
</div>
</template>
<template #dropdown>
<div class="mt-2 px-4 py-6 w-screen bg-white rounded shadow-xl" style="maxWidth: 600px">
<h1>Modal Content</h1>
<button id="close" class="btn-indigo" type="button">Hide Modal</button>
</div>
</template>
</dropdown>
</div>
</div>
</div>
</template>
<script>
import { Head } from '#inertiajs/inertia-vue3'
import Dropdown from '#/Shared/Dropdown'
export default {
components: {
Head,
Dropdown,
},
}
</script>
** And Dropdown Component **
<template>
<button type="button" #click="show = true">
<slot />
<teleport v-if="show" to="#dropdown">
<div>
<div style="position: fixed; top: 0; right: 0; left: 0; bottom: 0; z-index: 99998; background: black; opacity: 0.2" #click="show = false" />
<div ref="dropdown" style="position: absolute; z-index: 99999" #click.stop="show = !autoClose">
<slot name="dropdown" />
</div>
</div>
</teleport>
</button>
</template>
<script>
import { createPopper } from '#popperjs/core'
export default {
props: {
placement: {
type: String,
default: 'bottom-end',
},
autoClose: {
type: Boolean,
default: true,
},
},
data() {
return {
show: false,
}
},
watch: {
show(show) {
if (show) {
this.$nextTick(() => {
this.popper = createPopper(this.$el, this.$refs.dropdown, {
placement: this.placement,
modifiers: [
{
name: 'preventOverflow',
options: {
altBoundary: true,
},
},
],
})
})
} else if (this.popper) {
setTimeout(() => this.popper.destroy(), 100)
}
},
},
mounted() {
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
this.show = false
}
})
},
}
</script>

(Vue.js) I want to make image 1 pop up when I click on image 1, and I want image 2 to pop up when I click on image 2

There are several images. I want to make image 1 pop up when I click on image 1, and I want image 2 to pop up when I click on image 2. Can I solve it using the index in class?
Carousel.vue
<template>
<div v-for="(item, index) in items" :key="index">
<img :src="item.thumbnail" />
<button type="button" #click="imgClick()" :class="`img-index--${index}`">button-{{ index }}</button>
</div>
<Modal v-if="showModal" #close="showModal = false">
<div slot="body" v-for="(item, index) in items" :key="index">
<img :src="item.thumbnail" :class="`img-index--${index}`"/>
</div>
</Modal>
</template>
<script>
import Modal from './Modal.vue'
export default {
props: {
items: { type: Array, default: () => [] }
},
data() {
return {
showModal: false
}
},
methods: {
imgClick() {
this.showModal = !this.showModal;
}
},
components: {
Modal: Modal
}
}
</script>
You can build a new data from your items prop and inject a new property show: false for each image.
You don't need 2 v-for loops this way. You can put the Modal component inside the first loop and just use the item.show to display or not the modal.
<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>
Fiddle example here
Note: you can wrap the thumbnail inside a div to manage the click image.

How to Add Vue Component as an HTML Tag

I'm new to using VueJS, and I'm working on a learning project right now.
I have a component called "Draggable", and another one called "ModalPage".
"ModalPage" is as follows:
The code for this page is below:
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<button #click="propagate">Propagate</button>
<h3>Vue Modals</h3>
<ul id="list">
</ul>
<!-- <div v-html="template"></div> -->
</div>
</template>
<script>
import draggable from '#/components/Draggable.vue';
export default {
name: 'ModelPage',
components: {
draggable,
},
data () {
return {
msg: 'Welcome to the Modal Popup Page',
template: `<draggable></draggale>`,
}
},
methods: {
propagate () {
// console.log("propagated")
list.append(`<draggable></draggale>`)
}
}
}
</script>
I also have a component called "Draggable" and its code is as follows:
<template>
<div id="app">
<VueDragResize :isActive="true" :w="200" :h="200" v-on:resizing="resize" v-on:dragging="resize">
<h3>Hello World!</h3>
<p>{{ top }} х {{ left }} </p>
<p>{{ width }} х {{ height }}</p>
</VueDragResize>
</div>
</template>
<script>
import VueDragResize from 'vue-drag-resize';
export default {
name: 'app',
components: {
VueDragResize
},
data() {
return {
width: 0,
height: 0,
top: 0,
left: 0
}
},
methods: {
resize(newRect) {
this.width = newRect.width;
this.height = newRect.height;
this.top = newRect.top;
this.left = newRect.left;
}
}
}
</script>
What i want to do is to be able to click the "Propagate" button on the page, and have a <draggable></draggable> html element appended to the page, as follows:
<ul id="list">
<draggable></draggable>
</ul>
I'm completely stumped with this. Can anyone help me please?
v-if will do your job
Updated
You can use v-for as discussion:
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<button #click="propagate">Propagate</button>
<h3>Vue Modals</h3>
<ul id="list">
<draggable v-for="index in count" :key="index>
</draggable>
</ul>
<!-- <div v-html="template"></div> -->
</div>
</template>
<script>
import draggable from '#/components/Draggable.vue';
export default {
name: 'ModelPage',
components: {
draggable,
},
data () {
return {
msg: 'Welcome to the Modal Popup Page',
template: `<draggable></draggale>`,
count: 0
}
},
methods: {
propagate () {
// console.log("propagated")
this.count++
}
}
}
</script>