I am trying to use Swiper with "vue": "^2.6.11", but it throws runtime errors. I followed the guide from https://swiperjs.com/vue, and changed the imports to:
// Import Swiper Vue.js components
import { Swiper, SwiperSlide } from 'swiper/vue/swiper-vue.js';
// Import Swiper styles
import 'swiper/swiper-bundle.css';
Error message:
Property or method "onSwiper" is not defined on the instance but referenced during render, Invalid handler for event "swiper": got undefined , Failed to mount component: template or render function not defined
The Swiper components only work with Vue 3. Those components cannot be used in Vue 2, but the Swiper API can be used directly instead:
Apply a template ref on the target Swiper container element in the template.
In the component's mounted() hook, initialize an instance of Swiper, passing the template ref and Swiper options that include selectors for the pagination/navigation elements in the template.
<script>
import Swiper, { Navigation, Pagination } from 'swiper'
import 'swiper/css'
import 'swiper/css/navigation'
import 'swiper/css/pagination'
export default {
mounted() {
2️⃣
new Swiper(this.$refs.swiper, {
// configure Swiper to use modules
modules: [Navigation, Pagination],
// Optional parameters
loop: true,
// If we need pagination
pagination: {
el: '.swiper-pagination',
},
// Navigation arrows
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
// And if we need scrollbar
scrollbar: {
el: '.swiper-scrollbar',
},
})
},
}
</script>
<template>
<!-- Slider main container -->
<div 1️⃣ ref="swiper" class="swiper">
<!-- Additional required wrapper -->
<div class="swiper-wrapper">
<!-- Slides -->
<div class="swiper-slide">Slide 1</div>
<div class="swiper-slide">Slide 2</div>
<div class="swiper-slide">Slide 3</div>
</div>
<!-- If we need pagination -->
<div class="swiper-pagination"></div>
<!-- If we need navigation buttons -->
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
<!-- If we need scrollbar -->
<div class="swiper-scrollbar"></div>
</div>
</template>
<style scoped>
.swiper-slide {
display: flex;
justify-content: center;
align-items: center;
}
</style>
demo
Make some additions for #tony19's good answer.
Here is a demo example project: Live Demo
<script>
import Swiper, { Navigation, Pagination, Autoplay } from 'swiper'
import 'swiper/swiper-bundle.min.css'
export default {
data() {
return {
activeIndex: 0,
}
},
mounted() {
const SECOND = 1000 // milliseconds
new Swiper(this.$refs.swiper, {
modules: [Navigation, Pagination, Autoplay],
loop: true,
autoplay: {
delay: 3 * SECOND,
disableOnInteraction: false,
},
speed: 2 * SECOND,
pagination: {
el: '.swiper-pagination',
clickable: true,
},
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
on: {
slideChange: (swiper) => {
this.activeIndex = swiper.realIndex
},
},
})
},
}
</script>
Related
I'm trying to implement a simple world map in a Vue application. I have created a MapContainer component that is then imported into my main app. Below is the code for MapContainer.vue:
<template>
<div
ref="map-root"
style="width: 100%, height: 100%">
</div>
</template>
<script>
import View from 'ol/View';
import Map from 'ol/Map';
import TileLayer from 'ol/layer/Tile';
import OSM from 'ol/source/OSM';
import 'ol/ol.css';
export default {
name: 'MapContainer',
components: {},
props: {},
mounted() {
new Map({
target: this.$refs['map-root'],
layers: [
new TileLayer({
source: new OSM()
})
],
view: new View({
zoom: 0,
center: [0, 0],
constrainResolution: true
})
});
}
}
</script>
<style>
</style>
I am registering the MapContainer component and then simply placing it inside a div in the parent component. When I import this component and try to use it, I get an empty div in place of the map. Does anyone know what I'm doing wrong?
Here is the parent component's code:
<template>
<div>
<map-container></map-container>
</div>
</template>
<script>
import MapContainer from '../mapping/MapContainter.vue';
export default {
components: {
'map-container': MapContainer
}
}
</script>
I fixed this by adding the following class to the div with the ref:
.map {
width: 100% !important;
height: 600px !important;
}
(The !important's might not be strictly necessary).
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
Hello I am having an issue with Nuxts ssr. I a trying to add 'vue-slick' to my web app and no matter what I do it continues to show "window is not defined".
As you can see I have tried multiple ways to allow vue-slick to be loaded on client side. Using plugins didn't help, using process.client in my component did not work as well.
Components/Carousel/Carousel.vue
<template>
<div class="carousel">
<Slick ref="slick" :options="slickOptions">
<a href="http://placehold.it/320x120">
<img src="http://placehold.it/320x120" alt="">
</a>
...
<a href="http://placehold.it/420x220">
<img src="http://placehold.it/420x220" alt="">
</a>
</Slick>
</div>
</template>
<script>
if (process.client) {
require('vue-slick')
}
import Slick from 'vue-slick'
export default {
components: {
Slick
},
data() {
return {
slickOptions: {
slidesToShow: 4
},
isMounted: false
}
},
methods: {
}
}
</script>
nuxt.config.js
plugins: [
{ src: '~/plugins/vue-slick', ssr: false }
],
plugins/vue-slick
import Vue from 'vue'
import VueSlick from 'vue-slick'
Vue.use(VueSlick);
Thanks for any help you can give!
So this is due to nuxt trying to render the slick component on the server side, even though you have set ssr: false in nuxt.config.
I have had this issue in other nuxt plugins and these steps should fix it.
in nuxt.config.js add this to your build object:
build: {
extend(config, ctx) {
if (ctx.isServer) {
config.externals = [
nodeExternals({
whitelist: [/^vue-slick/]
})
]
}
}
}
and in the page where you are trying to serve it you have to not mount the component until the page is fully mounted. So in your Components/Carousel/Carousel.vue set it up like this:
<template>
<div class="carousel">
<component
:is="slickComp"
ref="slick"
:options="slickOptions"
>
<a href="http://placehold.it/320x120">
<img src="http://placehold.it/320x120" alt="">
</a>
...
<a href="http://placehold.it/420x220">
<img src="http://placehold.it/420x220" alt="">
</a>
</component>
</div>
</template>
Then in your script section import your component and declare it like this:
<script>
export default {
data: () => ({
'slickComp': '',
}),
components: {
Slick: () => import('vue-slick')
},
mounted: function () {
this.$nextTick(function () {
this.slickComp = 'Slick'
})
},
}
</script>
Bascially this means the component isn't declared until the mounted function is called which is after all the server side rendering. And that should do it. Good luck.
I found another simple solution.
simply install "vue-slick" package to your nuxt project.
$ yarn add vue-slick
then component markup like below.
<template>
<component :is="slick" :options="slickOptions">
<div>Test1</div>
<div>Test2</div>
<div>Test3</div>
</component>
</template>
Finally set data and computed properties like this.
data() {
return {
slickOptions: {
slidesToShow: 1,
slidesToScroll: 1,
infinite: true,
centerMode: true,
},
};
},
computed: {
slick() {
return () => {
if (process.client) {
return import("vue-slick");
}
};
},
},
This solution prevents slick component importing globally as a plugin in nuxt config.
I added v-cloak directive on the top div in my component and added css as in docs, set a timeout but it does not work I can see else content and the form styles.
Component code:
<div class="form-in-wrap" v-cloak>
<ul id="example-1" v-if="reports.length>0">
<report-component v-for="report in reports" :report="report" :key="report.id" :options="options">
</report-component>
</ul>
</div>
Component script:
<script>
import ReportComponent from "./ReportComponent";
export default {
components: {ReportComponent},
data: function () {
return {
reports: {
type: Array,
},
report: {
...
},
}
},
created: function () {
var self = this
setTimeout(function () {
self.loadData('/reports')
}, 2000);
},
methods: {
loadData: function() {
get method
}
}
</script>
<style scoped>
[v-cloak] {
display: none !important;
}
</style>
All example creates a Vue instance, but I am using export default in my component. Also it is not a main component, it is included with router can this be the case why it does not work?
I am unsure what I am doing wrong. I get the below error:
error message in console:
[Vue warn]: Failed to mount component: template or render function not defined.
found in
---> <VueChartjs>
<Chatjsvue> at src\components\vueChartjs\Chatjsvue.vue
<App> at src\App.vue
<Root>
index.js
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '#/components/HelloWorld'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
// component: HelloWorld
component: require('../components/HelloWorld.vue').default
}
]
})
App.vue
<template>
<div id="app" class="container">
<img src="./assets/logo.png" class="logo">
<!-- chartjs -->
<div class="chartjsvue">
<Chatjsvue></Chatjsvue>
</div>
<div class="clear"></div>
<!-- chartjs -->
</div>
</template>
<script>
import Chatjsvue from '#/components/vueChartjs/Chatjsvue'
export default {
name: 'App',
components: {
Chatjsvue
}
}
</script>
Chatjsvue.vue
<template src="../../views/chartjshtml/chartsjs.html"></template>
<script>
import Chartjsvue from '#/assets/javascripts/chartjs'
export default {
components: {
'vue-chartjs': Chartjsvue
}
};
</script>
chartsjs.html
<div class="wrapper">
<vue-chartjs></vue-chartjs>
</div>
chartjs.js
file is rmpty- no code in the file
What is the error referring to and what needs to be done to resolve it?
I think the problem is your chartjs.js is empty. You need to do:
import template from './chartjs.html' // in your questions it's chartsjs.html, please help to correct it
export default {
template: template
}
Your chartjs.js file shouldn't be empty. It should be a Vue component with a template that can be rendered. Any javascript can be written within the script tags themselves.
The components object should only contain the list of vue components you need to use in the current component. And each component must have a template.
Thank you to everyone who contributed in answering. The js file should not be empty. This is the complete code to display all the charts for chartjs
main.js [src/main.js]
import Vue from 'vue'
import App from './App'
import router from './router'
import ChartJsPluginDataLabels from 'chartjs-plugin-datalabels'
Vue.config.productionTip = false
require('./assets/stylesheets/application.css')
require('./assets/javascripts/application.js')
require('./assets/javascripts/chartjs.js')
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: {
App,
ChartJsPluginDataLabels
},
template: '<App/>'
})
App.vue [src/App.vue]
<template>
<div id="app" class="container">
<!-- chartjs -->
<div class="chartjsvue tab-content active" id="tab2">
<Chatjsvue></Chatjsvue>
</div>
<div class="clear"></div>
<!-- chartjs -->
</div>
</template>
<script>
import Chatjsvue from '#/components/vueChartjs/Chatjsvue'
export default {
name: 'App',
components: {
Chatjsvue
}
}
</script>
Chatjsvue.vue [src/components/Chatjsvue.vue]
<template src="../../views/chartjshtml/chartsjs.html"></template>
<script>
import Chartjsbarvue from '#/assets/javascripts/chartjsbar'
export default {
components: {
'vue-chartbarjs': Chartjsbarvue
},
mounted(){
console.log('Data is chartjs',this)
},
methods: {},
}
</script>
chartsjs.html [src/views/chartjshtml/chartsjs.html]
<div class="wrapper">
<div class="chart_header">chartjs bar chart</div>
<vue-chartbarjs></vue-chartbarjs>
</div>
chartjsbar.js [src/assets/javascripts/chartjsbar.js]
/*===================================================
File: Chartsjsvue.vue
===================================================*/
import { Bar } from 'vue-chartjs'
export default {
extends: Bar,
data () {
return {
datacollection: {
labels: ['VueJs', 'EmberJs', 'ReactJs', 'AngularJs'],
datasets: [
{
label: '1st Dataset hello',
backgroundColor: 'rgba(52,65,84, 0.3)',
bordercolor: '#344154"',
hoverBackgroundColor: "#344154",
data: [40, 20, 12, 39]
},
{
label: '2nd Dataset',
backgroundColor: 'rgba(130,191,163, 0.5)',
bordercolor: '#82BFA3"',
hoverBackgroundColor: "#82BFA3",
data: [50, 70, 22, 55]
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
datalabels: {
display: false
}
}
}
}
},
mounted () {
this.renderChart(this.datacollection, this.options)
}
}