How to create vertical slider with Swiper Vue.js? - slider

I am creating simple app using Vue3, also I am using Swiprer.js for vue, documentation of swiper.js (for vue3) is incomprehensible to me, I have imported all modules in my application, but now i want to create vertical scrollable slider, like picture below, my swiper component looks like this:
but still can't create vertical scrollable slider, this what my template looks like, any solutions?
<template>
<div class="container-main-slider">
<div class="container-main-slider__inner" id="sliderBox">
<swiper
:slides-per-view="1"
:space-between="20"
:direction="vertical"
:pagination="{ clickable: true}"
>
<swiper-slide>
<img :src="dynamic content">
</swiper-slide>
</swiper>
</div>
</div>
</template>
<script>
/* swiper slider imports */
import { Swiper, SwiperSlide } from "swiper/vue";
import SwiperCore, { A11y, Autoplay, Pagination } from "swiper";
import "swiper/swiper.scss";
import "swiper/swiper.scss";
import 'swiper/components/pagination/pagination.scss';
SwiperCore.use([A11y, Autoplay,Pagination]);
export default {
data(){
return{
fixedheader:false,
}
},
components: {
Swiper,
SwiperSlide,
},
methods:{
showVideo(){
this.$store.commit("CheckvideoVisibility", false)
},
onSlideChange() {
console.log('slide change');
},
},
};
</script>
`

I have a solution
If someone faced the same problem: you just need to put 'vertical' in single quotes.
It will look like this in Vue template:
<swiper
:slides-per-view="1"
:space-between="0"
:direction="'vertical'"
>

Related

How to use Swiper.js(version 8+) in Nuxt(2.15.8)

First I tried this as showed in official Swiper.js website for Vue 3 demo
<template>
<swiper
:effect="'coverflow'"
:grabCursor="true"
:centeredSlides="true"
:slidesPerView="'auto'"
:coverflowEffect="{
rotate: 50,
stretch: 0,
depth: 100,
modifier: 1,
slideShadows: true,
}"
:pagination="true"
:modules="modules"
class="mySwiper"
>
<swiper-slide v-for="card in cards"
><img
:src="card.image" /></swiper-slide>
</swiper>
</template>
<script>
// Import Swiper Vue.js components
import { Swiper, SwiperSlide } from "swiper/vue";
// Import Swiper styles
import "swiper/css";
import "swiper/css/effect-coverflow";
import "swiper/css/pagination";
import "./style.css";
// import required modules
import { EffectCoverflow, Pagination } from "swiper";
export default {
props: ['cards']
setup() {
return {
modules: [EffectCoverflow, Pagination],
};
},
};
</script>
And it did not work.
Then I tried to import it as a plugin in plugins folder of nuxt:
import Vue from 'vue';
import { Swiper, EffectCoverflow, Pagination } from "swiper";
const swiper = {
install(Vue, options) {
Vue.prototype.$swiper = Swiper;
Vue.prototype.$swiperModules = {
EffectCoverflow,
Pagination,
};
}
};
Vue.use(swiper);
And registred it in nuxt.js.config as: src: './plugins/swiper.client.js', mode: 'client'
And tried to use it in my component like this:
<template>
<Swiper>
<SwiperSlide v-for="card in cards" :key="card.id">
<NuxtLink :to="`products/${card.id}`" class="card">
<img
:src="require(card.image)"
alt="image"
class="image"
/>
<h3 class="header">{{ card.title }}</h3>
<p class="snippet">{{ card.snippet }}</p>
</NuxtLink>
</SwiperSlide>
</Swiper>
</template>
<script>
export default {
props: ['cards'],
mounted() {
this.swiper = new this.$swiper('.swiper', {
loop: true,
// configure Swiper to use modules
modules: [
this.$swiperModules.Pagination,
this.$swiperModules.EffectCoverflow,
],
})
},
}
</script>
And it is still not working, What am I doing wrong?
Can anyone help with it?
TLDR: Nuxt2 and Swiper8 are not compatible.
Swiper v8.0.0 is almost 1 year old: https://github.com/nolimits4web/swiper/releases/tag/v8.0.0
2 years ago, nolimits4web aka the main maintainer of the package said
Swiper Vue.js components are compatible only with new Vue.js version 3
Easy to say that the v8 of Swiper is definitely not compatible with Nuxt2 (using Vue2).
Even if there was a hack, it would be quite dirty and not the thing that I would recommend overall.
swiper#8.4.5 is also 38.7kB gzipped, which is quite on the heavy side of things.
If you're using all of its features and ready to upgrade to Nuxt3 (which might not be trivial), then you could maybe proceed.
Otherwise, you could maybe design your own carousel component or check the ones available here: https://github.com/vuejs/awesome-vue#carousel
I'm guessing that there are some projects with Nuxt2 support still, not too heavy and still maintained that could satisfy your needs.

How to create a Vue Tooltip component using BootstrapVue tooltip

I'm new to Vue and I don't use Bootstrap often so please pardon my newbie question, I'm trying to create a Vue tooltip component, I've created one to behave the way I wanted using css, however, I'm running into some accessibility issues, so I decided to use the BootstrapVue tooltip instead, but I don't know how I would create this component with Bootstrap.
This is basically my Tooltip.vue component using css:
<template>
<div :class="`tooltip ${position}`">
<slot></slot>
<span class="tooltip-text">{{content}}</span>
</div>
</template>
<script>
export default {
name: 'Tooltip',
props: {
position: String,
content: String,
}
};
</script>
<style lang="scss">
.tooltip {
.......
</style>
Then I import and use my component in other places like this:
<tooltip position="right" content="Right tooltip">Hover me</tooltip>
And I have created a TooltipBootstrap.vue component wanting to have the same structure but using Bootstrap, but I don't know how that would go, here is what I started:
I npm installed bootstrap-vue
<template>
<div>
<button v-b-tooltip.hover.${position}="'${content}'"></button>
</div>
</template>
<script>
import VBTooltip from 'bootstrap-vue';
export default {
name: 'TooltipBootstrat',
components: {
VBTooltip,
},
props: {
position: String,
content: String,
}
};
</script>
I'm reading the bootstrap documentation: https://bootstrap-vue.org/docs/directives/tooltip, but I don't know if I'm using this the way it's supposed to be used, so I'm a little lost and would appreciate any help/advice, thanks!
BootstrapVue provide <b-tooltip> component and v-b-tooltip directive (preferred method from document). You can play around in the document.
In simple words, you can use v-b-tooltip directive on any element which is very convenient. but for <b-tooltip> component you have to set target to identify the target to active the tooltip.
So in your case you can do something like:
<template>
<div v-b-tooltip="{ title: content, placement: position }">
<slot></slot>
</div>
</template>
<script>
import { VBTooltip } from 'bootstrap-vue'
export default {
name: 'Tooltip',
directives: {
'b-tooltip': VBTooltip,
},
props: {
position: String,
content: String,
}
};
</script>

Vue2-leaflet map not showing properly in BoostrapVue modal

Here's my problem - a Vue2 leaflet map does not render correctly in BootstrapVue modal.
Here's what it looks like visually (it should show just the ocean)
<template>
<div>
<b-modal size="lg" :visible="visible" #hidden="$emit('clear')" title="Event details">
<div class="foobar1">
<l-map :center="center" :zoom="13" ref="mymap">
<l-tile-layer :url="url" :attribution="attribution"></l-tile-layer>
<l-marker :lat-lng="center"></l-marker>
</l-map>
</div>
<template slot="modal-footer">
<b-btn variant="danger" #click="deleteEventLocal(event.id)">Delete</b-btn>
</template>
</b-modal>
</div>
</template>
<script>
import * as moment from "moment";
import { LMap, LMarker, LTileLayer } from "vue2-leaflet";
import { deleteEvent } from "./api";
import "vue-weather-widget/dist/css/vue-weather-widget.css";
import VueWeatherWidget from "vue-weather-widget";
export default {
data() {
return {
center: L.latLng(event.latitude, event.longitude),
url: "http://{s}.tile.osm.org/{z}/{x}/{y}.png",
attribution:
'© OpenStreetMap contributors'
};
},
props: {
visible: {
type: Boolean
},
event: {
required: true,
type: Object
}
},
methods: {
async deleteEventLocal(id) {
await deleteEvent(id);
this.$emit("refresh");
this.$emit("clear");
}
},
components: {
weather: VueWeatherWidget,
LMap,
LMarker,
LTileLayer
}
};
</script>
As you can see there aren't any CSS rules that could make the map spill outside the modal as it does. Which is weird.
I'm kinda asking this question to answer it myself as I couldn't find a solution before.
There were 3 issues because of which this was happening.
1. First - I forgot to load the leaflet css into main.js - this is why the leaflet map was somehow outside the modal.
//src/main.js
import '#babel/polyfill';
import Vue from 'vue';
import './plugins/bootstrap-vue';
import App from './App.vue';
import router from './router';
import store from './store';
//above imports not important to this answer
import 'leaflet/dist/leaflet.css'; //<--------------add this line
new Vue({
router,
store,
render: h => h(App),
}).$mount('#app');
2. Now the map may disappear. Set a width and height on the l-map component's container. I used a class but you can use style="" etc.
<div class="foobar1"> <!-- <--- Add a class on l-map's container -->
<l-map :center="center" :zoom="13">
<l-tile-layer :url="url" :attribution="attribution"></l-tile-layer>
<l-marker :lat-lng="center"></l-marker>
</l-map>
</div>
<style lang="scss">
.foobar1 { /* <--- class we added above */
width: 100%;
height: 400px;
}
</style>
3. Now your map will render within the modal but if you move the map's view, you'll see that leaflet does not download the map's squares in time.
You will see something like this:
To fix this:
create an event handler on b-modal for the #shown event.
<b-modal
#shown="modalShown"
#hidden="$emit('clear')"
size="lg"
:visible="visible"
title="Event details"
>
I called mine modalShown.
Then, add a ref attribute to your l-map. I called mine mymap.
<l-map :center="center" :zoom="13" ref="mymap"> <!-- ref attribute added to l-map -->
<l-tile-layer :url="url" :attribution="attribution"></l-tile-layer>
<l-marker :lat-lng="center"></l-marker>
</l-map>
Then, create a modalShown method in the Vue methods for your view/component and call invalidateSize() inside.
export default {
data() {
//some data here
}
methods: {
modalShown() {
setTimeout(() => {
//mapObject is a property that is part of leaflet
this.$refs.mymap.mapObject.invalidateSize();
}, 100);
}
}
}
Now everything should be fine:
map should not spill outside the modal
map should be visible (duh)
map squares should be downloaded when within map body
Here's my full code, it contains some stuff specific to my app but overall it contains all of the code snippets above.
Addtional to Artur Tagisow answer
You can also use this approach to your parent component if your map is in child component.
export default {
data() {
//some data here
}
methods: {
modalShown() {
setTimeout(() => {
window.dispatchEvent(new Event("resize"));
}, 100);
}
}
}
For vue.js and nuxt.js developers , probably it's because of using v-show or v-if
!in your case display none happening by bootstrap modal
but dont worry the only thing u have to do is using client-only (its like ssr but for new version of js frameworks like nuxt or vue):
<client-only>
<div id="bootstrapModal">
<div id="map-wrap" style="height: 100vh">
<l-map :zoom=13 :center="[55.9464418,8.1277591]">
<l-tile-layer url="http://{s}.tile.osm.org/{z}/{x}/{y}.png"></l-tile-layer>
<l-marker :lat-lng="[55.9464418,8.1277591]"></l-marker>
</l-map>
</div>
</div>
</client-only>
ps: if still not loaded in iphone browsers it's probably because of geolocation

render a Vue slot in a layout from component

I'm having problems with a named slot. This seems like it should work. In the code below I'm trying to use a named slot "sidebar". I would expect my sidebar slot content to show up between the Sidebar starts and Sidebar ends text but nothing shows up in that slot. Everything renders in the main slot.
Here's my code.
route...
{
path: "/test",
name: "test",
meta: {
layout: "test-layout"
},
component: () =>
import("#/pages/Test.vue")
},
and App.vue template...
<template>
<div id="app">
<component :is="layout">
<router-view />
</component>
</div>
</template>
and test-layout...
<template>
<div>
<div>
<h1>Sidebar starts</h1>
<slot name="sidebar"/>
<h1>Sidebar ends</h1>
</div>
<div class="container">
<h1>Content starts</h1>
<slot/>
<h1>Content ends</h1>
</div>
</div>
</template>
and page Test.vue...
<template>
<test-layout>
<span slot="sidebar">sidebar slot content {{forecast.todaySummary}}</span>
<div>main slot content {{forecast.currentSummary}}</div>
</test-layout>
</template>
<script>
import api from "#/js/web-services";
export default {
data() {
return {
forecast: null
};
},
created() {
api.getDailyForecast().then(response => {
this.forecast = response.data;
});
}
};
</script>
and the import in my main.js
import TestLayout from "./layouts/test-layout.vue";
Vue.component('test-layout', TestLayout);
Why isn't my sidebar slot working?
UPDATE
If I get rid of the two lines in main.js and add
import TestLayout from "#/layouts/test-layout.vue";
and
export default {
components: { TestLayout },
data() {...
to Test.vue then it works.
In your router file you are using layout: "test-layout" this means what ever comes in your vue component will be rendered in base test-layout.
There are two ways as far as I know to render the layouts.
Do not define layout in router file and on parent component define named slots like this<slot #header></slot><slot #body></slot> then every slot will be rendered within this (test-layout) layout, and then in your each component extend like this <test-layout><template header>Header content</template><template body>Body content</template></test-layout>.
Defining layout in router file like you did, you can not further use in slots in that layout, you can just import other components e.g <template><Component1><Component2> </template>

Vue.js v-if toggle for show/hide nav

I am trying to make a nav that you can show/hide toggle by clicking on an button. Can anyone tell me what I am doing wrong here? I am still learning Vue so any help would be appreciated. Thanks.
Here's the code from navigation.vue:
<template>
<div>
<nav v-if="seen">
<ul>
<li>front</li>
<li>contact</li>
</ul>
</nav>
<p><button v-on:click="seen = !seen">Toggle</button></p>
</div>
</template>
<script>
export default {
data: {
seen: true
}
}
</script>
Importing the navigation in App.vue:
<template>
<main>
<app-navigation></app-navigation>
<app-front></app-front>
<app-footer></app-footer>
</main>
</template>
<script>
import Navigation from './components/navigation.vue'
import Front from './components/front.vue'
import Footer from './components/footer.vue'
export default {
components: {
'app-navigation': Navigation,
'app-front': Front,
'app-footer': Footer
},
data () {
return {
}
}
}
</script>
With components, your data property needs to be a function.
data(){
return {
seen: true
}
}