v-if is not updated when importing a 3rd party script with vue-meta - vue.js

I want to use vue-meta to import a 3rd party SwiperJS script into my nuxt website.
The script is loaded only after mounted()-hook is called so I also initialize it on update(). This kind of works but the v-if on the wrapper does not update.
<template>
<section
class="swiper"
v-if="isSwiperLoaded"
>
<div class="swiper-wrapper"> ... </div>
</section>
</template>
<script>
export default {
data() {
return { swiperLoaded: false, swiperInitialized: false }
},
computed: {
isSwiperLoaded: {
get() {
return this.swiperLoaded
},
set(value) {
this.swiperLoaded = value
},
},
isSwiperInitialized: {
get() {
return this.swiperInitialized
},
set(value) {
this.swiperInitialized = value
},
},
},
head() {
return {
script: [
{
hid: 'swiper',
src: 'https://cdn.jsdelivr.net/npm/swiper#8/swiper-bundle.min.js',
defer: true,
// Changed after script load
callback: () => {
this.isSwiperLoaded = true
},
},
],
link: [
{
rel: 'stylesheet',
type: 'text/css',
href: 'https://cdn.jsdelivr.net/npm/swiper#8/swiper-bundle.min.css',
},
],
}
},
methods: {
initSwiper() {
const swiperOptions = {
...
}
let swiper = new Swiper(this.$el, swiperOptions)
},
},
mounted() {
if (!this.isSwiperInitialized && this.isSwiperLoaded) {
console.log('INIT LOADED')
this.initSwiper()
this.isSwiperInitialized = true
}
},
updated() {
if (!this.isSwiperInitialized && this.isSwiperLoaded) {
console.log('UPD LOADED')
this.initSwiper()
this.isSwiperInitialized = true
}
},
}
</script>
Also I noticed the computed values are normally for getting and setting values from store and not local values. Maybe there is an easier way of updating the variables.

Related

Not receiving duration of video using videojs in Vue component

Using Vue 2 and Video.js 7 I am trying to show a video through a conditional component (sometimes I need to show an iframe instead of videojs). The video (.m3u8 and .mp4) loads but the duration is shown as "--:-" and clicking the seek bar gives an error in the console:
VIDEOJS: TypeError: Failed to set the 'currentTime' property on 'HTMLMediaElement': The provided double value is non-finite.
at Html5.setCurrentTime (video.es.js?c063:19084:1)
at set (video.es.js?c063:9410:1)
at Player.eval (video.es.js?c063:22620:1)
at Player.ready (video.es.js?c063:3756:1)
at Player.techCall_ (video.es.js?c063:22618:1)
at Player.currentTime (video.es.js?c063:22874:1)
at SeekBar.handleMouseMove (video.es.js?c063:13106:1)
at SeekBar.handleMouseDown (video.es.js?c063:12181:1)
at SeekBar.handleMouseDown (video.es.js?c063:13055:1)
at ProgressControl.handleMouseDown (video.es.js?c063:13448:1) Video is not ready. (Video.js)
I noticed that videojs returns NaN as duration, even after the metadata loaded. Other than that I have no clue what's going wrong or how I can make it so videojs has a useable duration.
main.vue
<template>
<div>
<videoPlayer
:playerData="data.player.VIDEO_PLAYER"
ref="videoPlayer"
#playerReady="playerReady"
></videoPlayer>
</div>
</template>
<script>
import videoPlayer from "components/player";
export default {
name: "Name",
data() {
return {
playerIsReady: false,
currentVideoTime: 0,
};
},
components: {
videoPlayer,
},
methods: {
playerReady(status) {
this.playerIsReady = status;
},
checkTimePlayer() {
const self = this;
this.playerInterval = setInterval(function () {
self.currentVideoTime = self.$refs.videoPlayer
? self.$refs.videoPlayer.getCurrentTime()
: 0;
}, 500);
},
},
watch: {
playerIsReady(newVal) {
if (newVal) {
this.checkTimePlayer();
} else if (this.playerInterval) {
clearInterval(this.playerInterval);
}
},
},
};
</script>
player.vue
<template>
<div>
<videoPlayerVideojs
v-if="playerData.stream_type == 'videojs'"
:options="options"
ref="videoPlayerVideomeojs"
#playerReady="playerReady"
></videoPlayerVideojs>
<videoPlayerSomethingElse
v-else-if="playerData.stream_type == 'somethingElse'"
:options="options"
ref="videoPlayerSomethingElse"
></videoPlayerSomethingElse>
</div>
</template>
<script>
import videoPlayerVideojs from "components/playerVideojs";
import videoPlayerSomethingElse from "components/playerSomethingElse";
export default {
name: "VideoPlayer",
props: {
playerData: {
type: Object,
required: true,
},
},
components: {
videoPlayerVideojs,
videoPlayerSomethingElse,
},
data() {
return {
player: null,
options: {},
};
},
mounted() {
if (this.playerData.stream_type === "videojs") {
this.options = {
preload: "auto",
fluid: true,
autoplay: true,
controls: true,
sources: [
{
src: "https://archive.org/download/BigBuckBunny_124/Content/big_buck_bunny_720p_surround.mp4",
type: "video/mp4",
},
],
};
this.player = this.$refs.videoPlayerVideojs;
} else if (this.playerData.stream_type === "somethingElse") {
// something ... else
this.player = this.$refs.videoPlayerSomethingElse;
}
},
methods: {
getCurrentTime() {
return this.player.getCurrentTime();
},
playerReady(status) {
this.$emit("playerReady", status);
},
},
};
</script>
playerVideojs.vue
<template>
<div>
<video ref="video" class="video-js"></video>
</div>
</template>
<script>
import videojs from "video.js";
import "video.js/dist/video-js.css";
export default {
name: "VideoPlayerVideojs",
props: {
options: {
type: Object,
required: true,
},
},
data() {
return {
player: null,
ready: false,
currentVideoTime: 0,
};
},
async mounted() {
const self = this;
await this.$nextTick();
this.player = videojs(
this.$refs.video,
this.options
);
this.player.on("ready", () => {
console.log("ready");
console.log(this.player.readyState());
console.log(this.player.duration());
console.log(this.player.liveTracker.seekableEnd());
});
this.player.on("loadedmetadata", () => {
console.log("loaded metadata");
self.ready = true;
console.log(this.player.readyState());
console.log(this.player.duration());
console.log(this.player.liveTracker.seekableEnd());
});
this.player.on("durationchange", () => {
console.log("duration change");
console.log(this.player.readyState());
console.log(this.player.duration());
console.log(this.player.liveTracker.seekableEnd());
});
},
watch: {
ready(newVal) {
this.$emit("playerReady", newVal);
},
},
methods: {
getCurrentTime() {
return this.player.currentTime();
},
},
beforeDestroy() {
if (this.player) {
this.player.dispose();
}
},
};
</script>
The console outputs
14:31:33.682 playerVideojs.vue?5887:40 ready
14:31:33.682 playerVideojs.vue?5887:42 0
14:31:33.682 playerVideojs.vue?5887:43 NaN
14:31:33.682 playerVideojs.vue?5887:44 Infinity
14:31:34.085 playerVideojs.vue?5887:54 duration change
14:31:34.085 playerVideojs.vue?5887:56 1
14:31:34.085 playerVideojs.vue?5887:57 NaN
14:31:34.085 playerVideojs.vue?5887:58 596.504
14:31:34.085 playerVideojs.vue?5887:47 loaded metadata
14:31:34.085 playerVideojs.vue?5887:49 1
14:31:34.085 playerVideojs.vue?5887:50 NaN
14:31:34.086 playerVideojs.vue?5887:51 596.504
Via Vue devtools I've compared the player on the Vue page with a working player on a non-Vue page. And found that player.controlBar.durationDisplay.duration is "NaN" for the Vue player and a float for the non-Vue player.
Also when using an .m3u8 I notice that player.vhs.playlists.media_.id is 2 for the Vue player and 1 for the non-Vue player. Could it be that the media I'm trying to load is the second time the player tries to load media?
Other than that the players are pretty much identical as far as I can see.

How to use v-model in QuillJS in vue 3 app?

If I try to use v-model in the div with editor reference it gives me this error:
"'v-model' directives aren't supported on elements."
<div ref="editor"></div>
<script>
import Quill from "quill";
export default {
data() {
return {
description: "", // variable that I'm trying to use in v-model
options: {
modules: {
toolbar: ["bold", "italic", { list: "ordered" }, { list: "bullet" }],
},
theme: "snow",
},
};
},
watch: {
isDescription(newVal, oldVal) {
if (newVal == true) {
setTimeout(() => {
new Quill(this.$refs.editor, this.options);
}, 1);
}
},
},
};
</script>

Initialization of variables with Vuex

I made a VueJS 3 project with VueX to store the data.
When I print the variable data.doughnutChart.data in the following code it displays
{ "labels": [ "OK", "WARNING", "ERROR" ], "datasets": [ {
"backgroundColor": [ "#d4efdf", "#fdebd0", "#fadbd8" ], "data": [ 3,
1, 2 ] } ] }
But the graph doesn't use these data [3,1,2], the graph uses the values of the initialization in the index.js of VueX.
Here my code :
<template>
{{data.doughnutChart.data}}
<div style="height:200px;width: 200px; position:center">
<vue3-chart-js
:id="data.doughnutChart.id"
:type="data.doughnutChart.type"
:data="data.doughnutChart.data"
:options="data.doughnutChart.options"
></vue3-chart-js>
</div>
</template>
<script>
import Vue3ChartJs from '#j-t-mcc/vue3-chartjs'
export default {
name: 'App',
components: {
Vue3ChartJs,
},
beforeMount() {
this.$store.dispatch("getData");
},
computed: {
data() {
return {
doughnutChart: {
id: 'doughnut',
type: 'doughnut',
data: {
labels: ['OK', 'WARNING', 'ERROR'],
datasets: [
{
backgroundColor: [
'#d4efdf',
'#fdebd0',
'#fadbd8'
],
data: [this.$store.state.nbOk, this.$store.state.nbWarning, this.$store.state.nbError]
}
]
},
options:
{
plugins: {
legend: {
display: false
},
title: {
display: true,
text: 'Current situation'
}
},
}
}
}
}
}
}
</script>
I read the value in my index.js (VueX) :
import axios from 'axios'
import { createStore } from 'vuex'
export default createStore({
state: {
data: [],
nbError : 0,
nbWarning : 0,
},
actions: {
getData({commit}){
axios.get('http://localhost:8080/data/mock.json')
.then(res => {
commit('SET_DATA', res.data)
})}
},
mutations: {
SET_DATA(state, data){
state.data = data.data;
state.nbWarning = 0;
state.nbError = 0;
for (let i = 0; i < state.data.length; i++) {
if(state.data[i].status == 'WARNING'){
state.nbWarning += 1;
};
if(state.data[i].status == 'ERROR'){
state.nbError += 1;
};
};
}
})
However it works when, in my Vuejs project, I go in an other page and come back but not when I just open the project or refresh the page.
Do you know why ?
data property should be defined as computed in order to receive store changes:
<template>
{{data}}
</template>
<script>
export default {
data() {
return {
}
},
computed:{
data(){
return [this.$store.state.nbWarning, this.$store.state.nbError]
}
},
beforeMount() {
this.$store.dispatch("getData");
}
}
</script>

Unable to display chart using API call in chartjs in the context of Vuejs

Im trying to display chart using chartjs by calling API, but unable to do so.
Here s my LineChart.vue:
<script>
import {Line, mixins} from 'vue-chartjs' // We specify what type of chart we want from vue-chartjs and the mixins module
const { reactiveProp } = mixins
export default { //We are extending the base chart class as mentioned above
extends: Line,
mixins: [reactiveProp],
data () {
return {
options: { //chart options
lineTension: 0,
maintainAspectRatio: false,
legend: {
display: false
},
scales: {
yAxes: [
{
scaleLabel: {
display: false
},
ticks: {
beginAtZero: true,
// eslint-disable-next-line no-unused-vars
callback (value, index, values) {
return `${value }%`
}
}
}
],
xAxes: [
{
type: 'time',
time: {
unit: 'month'
},
scaleLabel: {
display: true,
labelString: ''
}
}
]
}
}
}
},
mounted () {
// this.chartData is created in the mixin
this.renderChart(this.chartData, this.options)
}
}
</script>
And here is my Home.vue where i have imported the LineChart:
<template>
<div class="chart">
<line-chart :chart-data="datacollection"></line-chart>
</div>
</template>
<script>
import LineChart from './LineChart'
import axios from 'axios'
import DateTime from 'luxon'
export default {
data () {
return {
date: {},
challenge: {},
datacollection: {}
}
},
component: {LineChart},
created() {
this.fillData()
},
mounted () {
this.fillData()
},
methods: {
fillData () {
axios.get('https://my_api_goes_here')
.then(response => {
const results = response.data
const dateresult = results.map(a => a.date)
const challengeresult = results.map(a => a.challenge)
this.date = dateresult
this.challenge = challengeresult
this.datacollection = {
labels: [this.date].map(labels => DateTime.fromMillis(labels * 1000).toFormat('MMM yyyy')),
datasets: [
{
data: [this.challenge],
label: 'Africa',
borderColor: '#7367F0'
}
]
}
})
.catch(error => {
console.log(error)
})
}
}
}
</script>
Dont know why the chart did not appear even though my other resources have been loaded from the API call, when i checked out my console, this is what error im getting:
TypeError: results.map is not a function
Please check out my logic and let me where the error is.

How can I re-initialise vue-awesome-swiper with a different loop setting?

I'm using vue-awesome-swiper for a product gallery. The user can select a different product and I want the gallery to update accordingly.
If the product has more than 1 image (most cases), I want the swiper to use the loop feature. If the product only has 1 image, I want it to work without the loop feature.
Assuming I'm able to capture when it should be true or false at the right time, how can I get the swiper to update or re-initialise with a different setting for the loop feature?
I've tried every combination of
this.swiper.destroy()
this.swiper.init()
new Swiper()
this.swiper.passedParams.loop = true/false
Whatever I do, either the loop feature stays, or the swiper doesn't actually initialise.
Edit: This is a really simplified version of the code. You will probably ask "why is he doing that?" on a couple of bits. It would make sense if you saw the full code, but its a bit too large to post here. Either way, it should not affect in responding with a solution for the loop problem.
<template>
<div class="pdp-main__gallery">
<swiper ref="mainSwiper"
id="pdpMainSwiper"
:options="mainSwiperOptions"
:data-swiper-group-id="swiperGroup"
:auto-update="true"
:auto-destroy="true"
>
<swiper-slide v-if="galleryProduct.LargeImage[1]">
<img ref="image" :data-href="galleryProduct.ExtraLargeImage[1]" :src="galleryProduct.LargeImage[1]" />
</swiper-slide>
<swiper-slide v-if="galleryProduct.LargeImage[2]">
<img ref="image" :data-href="galleryProduct.ExtraLargeImage[2]" :src="galleryProduct.LargeImage[2]" />
</swiper-slide>
<swiper-slide v-if="galleryProduct.LargeImage[3]">
<img ref="image" :data-href="galleryProduct.ExtraLargeImage[3]" :src="galleryProduct.LargeImage[3]" />
</swiper-slide>
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
</div>
</template>
<script>
import { Swiper, SwiperSlide, directive } from 'vue-awesome-swiper'
import 'swiper/css/swiper.css'
export default {
components: {
Swiper,
SwiperSlide,
},
props: {
swiperGroup: {
type: String,
required: true,
},
product: {
type: Object,
required: false,
default() {
return undefined;
},
},
},
data(){
return {
galleryProduct: {
LargeImage: {},
ExtraLargeImage: {},
},
mainSwiperOptions: {
loop: true,
watchOverflow: true,
setWrapperSize: false,
initialSlide: 0,
spaceBetween: 5,
centeredSlides: true,
slidesPerView: 'auto',
normalizeSlideIndex: false,
freeMode: false,
autoHeight: APP.Browser().data.isMobile,
followFinger: APP.Browser().data.isMobile,
pagination: {
el: '.swiper-pagination',
type: 'bullets',
clickable: true,
},
},
}
},
computed: {
mainSwiper() {
return this.$refs.mainSwiper.$swiper
},
shouldLoop() {
if(this.product.LargeImage1 && this.product.LargeImage2) return true; else return false;
},
},
created() {
this.setGalleryImages();
},
mounted() {
this.setGalleryImages();
},
watch: {
product(newVal, oldVal) {
if(newVal.LargeImage1 != oldVal.LargeImage1) {
this.setGalleryImages();
this.$nextTick(function() {
if(this.mainSwiperOptions.loop) this.mainSwiper.slideToLoop(0, 0);
this.thumbsSwiper.slideTo(0);
});
}
},
},
methods: {
setGalleryImages()
{
var needsReinit = false;
if(this.shouldLoop !== this.mainSwiperOptions.loop) {
this.mainSwiperOptions.loop = this.shouldLoop;
this.destroyMainSwiper();
needsReinit = true;
}
this.galleryProduct.LargeImage[1] = this.product.LargeImage1;
this.galleryProduct.ExtraLargeImage[1] = this.product.ExtraLargeImage1;
this.galleryProduct.LargeImage[2] = this.product.LargeImage2;
this.galleryProduct.ExtraLargeImage[2] = this.product.ExtraLargeImage2;
this.galleryProduct.LargeImage[3] = this.product.LargeImage3;
this.galleryProduct.ExtraLargeImage[3] = this.product.ExtraLargeImage3;
if(needsReinit) {
this.initMainSwiper();
}
return;
},
destroyMainSwiper() {
this.mainSwiper.destroy();
// this.mainSwiper.destroy(false, false);
},
initMainSwiper() {
//let swiper = this.mainSwiper.init();
var swiper = new Swiper('#pdpMainSwiper', this.mainSwiperOptions);
// this.$refs.mainSwiper.$swiper = swiper;
}
}
};
</script>
Help appreciated!