SwiperJS Lazy-Loading doesn't load images - vue.js

I've followed these steps, but the images still won't load. I read somewhere, to load the swiper after the images are loaded. So I init the swiper after the data is successfully received. But are the images already loaded then?
<div class="swiper">
<div class="swiper-wrapper">
<div v-for="image in property.images" :key="image.id" class="swiper-slide">
<img :data-src="image.filePath" class="swiper-lazy" />
<div class="swiper-lazy-preloader"></div>
</div>
</div>
</div>
async fetch() {
try {
const property = await this.$axios.$get(
`/api/get-specific-property/${this.$route.params.id}`
)
if (property.success) {
this.property = property.data
this.initSwiper()
}
} catch (err) {
console.log(err)
}
},
methods: {
initSwiper() {
Swiper.use([Lazy]) // Not sure if it's really needed - saw this somewhere
this.swiper = new Swiper('.swiper', {
lazy: true,
preloadImages: false,
slidesPerView: 1,
loop: false,
})
}
}

Related

Nuxt.js Hackernews API update posts without loading page every minute

I have a nuxt.js project: https://github.com/AzizxonZufarov/newsnuxt2
I need to update posts from API every minute without loading the page:
https://github.com/AzizxonZufarov/newsnuxt2/blob/main/pages/index.vue
How can I do that?
Please help to end the code, I have already written some code for this functionality.
Also I have this button for Force updating. It doesn't work too. It adds posts to previous posts. It is not what I want I need to force update posts when I click it.
This is what I have so far
<template>
<div>
<button class="btn" #click="refresh">Force update</button>
<div class="grid grid-cols-4 gap-5">
<div v-for="s in stories" :key="s">
<StoryCard :story="s" />
</div>
</div>
</div>
</template>
<script>
definePageMeta({
layout: 'stories',
})
export default {
data() {
return {
err: '',
stories: [],
}
},
mounted() {
this.reNew()
},
created() {
/* setInterval(() => {
alert()
stories = []
this.reNew()
}, 60000) */
},
methods: {
refresh() {
stories = []
this.reNew()
},
async reNew() {
await $fetch(
'https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty'
).then((response) => {
const results = response.slice(0, 10)
results.forEach((id) => {
$fetch(
'https://hacker-news.firebaseio.com/v0/item/' +
id +
'.json?print=pretty'
)
.then((response) => {
this.stories.push(response)
})
.catch((err) => {
this.err = err
})
})
})
},
},
}
</script>
<style scoped>
.router-link-exact-active {
color: #12b488;
}
</style>
This is how you efficiently use Nuxt3 with the useLazyAsyncData hook and a setInterval of 60s to fetch the data periodically. On top of using async/await rather than .then.
The refreshData function is also a manual refresh of the data if you need to fetch it again.
We're using useIntervalFn, so please do not forget to install #vueuse/core.
<template>
<div>
<button class="btn" #click="refreshData">Fetch the data manually</button>
<p v-if="error">An error happened: {{ error }}</p>
<div v-else-if="stories" class="grid grid-cols-4 gap-5">
<div v-for="s in stories" :key="s.id">
<p>{{ s.id }}: {{ s.title }}</p>
</div>
</div>
</div>
</template>
<script setup>
import { useIntervalFn } from '#vueuse/core' // VueUse helper, install it
const stories = ref(null)
const { pending, data: fetchedStories, error, refresh } = useLazyAsyncData('topstories', () => $fetch('https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty'))
useIntervalFn(() => {
console.log('refreshing the data again')
refresh() // will call the 'topstories' endpoint, just above
}, 60000) // every 60 000 milliseconds
const responseSubset = computed(() => {
return fetchedStories.value?.slice(0, 10) // optional chaining important here
})
watch(responseSubset, async (newV) => {
if (newV.length) { // not mandatory but in case responseSubset goes null again
stories.value = await Promise.all(responseSubset.value.map(async (id) => await $fetch(`https://hacker-news.firebaseio.com/v0/item/${id}.json?print=pretty`)))
}
})
function refreshData() { refreshNuxtData('topstories') }
</script>

Watch, Compare & post updated form data to API using Axios in Vue 3

I need help to complete my code.
This is what have done.
I am fetching options from API, so I have defined the initial state as
empty.
Once I have a response from API, I update the state of options.
My form is displayed once I have a response from API.
Now using v-bind I am binding the form.
Where I need help.
I need to watch for the changes in form. If the values of form elements are different from the state of the API response, I would like to enable the submit button.
When the save button is clicked, I need to filter the options that were changed & submit that form data to my pinia action called updateOptions.
Note: API handles post data in this way. Example: enable_quick_view: true
Thank you in advance.
options.js pinia store
import { defineStore } from 'pinia'
import Axios from 'axios';
import axios from 'axios';
const BASE_API_URL = adfy_wp_locolizer.api_url;
export const useOptionsStore = defineStore({
id: 'Options',
state: () => ({
allData: {},
options: {
enable_quick_view: null, // boolean
quick_view_btn_label: "", // string
quick_view_btn_position: "", // string
},
newOptions: {}, // If required, holds the new options to be saved.
message: "", // Holds the message to be displayed to the user.
isLoading: true,
isSaving: false,
needSave: false,
errors: [],
}),
getters: {
// ⚡️ Return state of the options.
loading: (state) => {
return state.isLoading;
},
},
actions: {
// ⚡️ Use Axios to get options from api.
fetchOptions() {
Axios.get(BASE_API_URL + 'get_options')
.then(res => {
this.alldata = res.data.settings;
let settings = res.data.settings_values;
/*
* Set options state.
*/
this.options.enable_quick_view = JSON.parse(
settings.enable_quick_view
);
this.options.quick_view_btn_label =
settings.quick_view_btn_label;
this.options.quick_view_btn_position = settings.quick_view_btn_position;
/*
* End!
*/
this.isLoading = false;
})
.catch(err => {
this.errors = err;
console.log(err);
})
.finally(() => {
// Do nothing for now.
});
},
// ⚡️ Update options using Axios.
updateOptions() {
this.isSaving = true;
axios.post(BASE_API_URL + 'update_options', payload)
.then(res => {
this.needSave = false;
this.isSaving = false;
this.message = "Options saved successfully!";
})
.catch(err => {
this.errors = err;
console.log(err);
this.message = "Error saving options!";
})
}
},
});
Option.vue component
<script setup>
import { onMounted, watch } from "vue";
import { storeToRefs } from "pinia";
import { Check, Close } from "#element-plus/icons-vue";
import Loading from "../Loading.vue";
import { useOptionsStore } from "../../stores/options";
let store = useOptionsStore();
let { needSave, loading, options, newOptions } = storeToRefs(store);
watch(
options,
(state) => {
console.log(state);
// Assign the option to the newOptions.
},
{ deep: true, immediate: false }
);
onMounted(() => {
store.fetchOptions();
});
</script>
<template>
<Loading v-if="loading" />
<form
v-else
id="ui-settings-form"
class="ui-form"
#submit="store.updateOptions()"
>
<h3 class="option-box-title">General</h3>
<div class="ui-options">
<div class="ui-option-columns option-box">
<div class="ui-col left">
<div class="label">
<p class="option-label">Enable quick view</p>
<p class="option-description">
Once enabled, it will be visible in product catalog.
</p>
</div>
</div>
<div class="ui-col right">
<div class="input">
<el-switch
v-model="options.enable_quick_view"
size="large"
inline-prompt
:active-icon="Check"
:inactive-icon="Close"
/>
</div>
</div>
</div>
</div>
<!-- // ui-options -->
<div class="ui-options">
<div class="ui-option-columns option-box">
<div class="ui-col left">
<div class="label">
<p class="option-label">Button label</p>
</div>
</div>
<div class="ui-col right">
<div class="input">
<el-input
v-model="options.quick_view_btn_label"
size="large"
placeholder="Quick view"
/>
</div>
</div>
</div>
</div>
<!-- // ui-options -->
<button type="submit" class="ui-button" :disabled="needSave == true">
Save
</button>
</form>
</template>
<style lang="css" scoped>
.el-checkbox {
--el-checkbox-font-weight: normal;
}
.el-select-dropdown__item.selected {
font-weight: normal;
}
</style>
In the watch function you can compare the new and old values. But you shuld change it to:
watch(options, (newValue, oldValue) => {
console.log(oldValue, newValue);
// compare objects
}, {deep: true, immediate: false};
Now you can compare the old with the new object. I think search on google can help you with that.
Hope this helps.

No compatible source was found for this media

Get that error when try to get stream from any HLS source.
I tried to add videojs-contrib-hls lib , but its dont help.
Maybe should i try some other player, and what player will properly work with hls sources?
<script>
import 'video.js/dist/video-js.css'
import {videoPlayer} from 'vue-video-player'
import videojs from 'video.js'
window.videojs = videojs
export default {
components: {
videoPlayer
},
data () {
return {
playerOptions: {
height: '700',
width: '1820',
controls: true,
sourceOrder: true,
hls: true,
sources: [{
// type: "video/mp4",
type: "application/x-mpegURL",
// src: "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
src: "http://127.0.0.1:8081/live/93UEctBmn/index.m3u8",
withCredentials: false,
}],
autoplay: false,
},
techOrder: ['html5'],
html5: { hls: { withCredentials: false } },
contentData: {}
}
},
mounted() {
this.$store.dispatch("content/getById", this.$route.params.contentId).then(
(res) => {
// this.message = data.message;
// this.snackbar = true;
if(res.data.error){
console.log(res.data.error);
return;
}
this.contentData = Array.isArray(res.data) ? res.data[0] : res.data;
},
(error) => {
// this.message = error.response.data.message || error;
// this.snackbar = true;
console.log(error)
}
);
}
}
</script>
<template>
<div class="content">
<div class="content-player">
<video-player class="vjs-custom-skin videoPlayer" :options="playerOptions" ></video-player>
</div>
<div class="content-info">
<div class="content-info_title">
{{contentData.streamTitle || 'loading'}}
</div>
</div>
<v-divider></v-divider>
<div class="content-author">
<div class="content-author_avatar">
<v-avatar
size="56"
>
<v-img
:src="'https://diploma-rtmp-bucket.s3.eu-central-1.amazonaws.com/'+contentData.author[0]._id || ''"
></v-img>
</v-avatar>
</div>
<div class="content-author_name">
{{contentData.author[0].username || 'undefined username'}}
</div>
</div>
</div>
</template>
Here is my page code. Can it be becouse of some CORS troubles?. I run it player on local machine
I used another player vue-vjs-hls. On this player hls work good, dont now why hls source not works at vue-video-player and video.js. What strange becouse vue-vjs-hls use video.js as core.

switch camera using MediaDevices.getUserMedia() in vuejs 2

I'm trying to develop a website where i can switch camera from chrome in mobile devices. Current im using vuejs 2 framework and using MediaDevices.getUserMedia() to take image. From here i understand how am i gonna use my code. Individually both of front and back camera working. But where im trying to switch between then its not working. Here is my code:
<template>
<div class="container" id="scanIdCardPage">
<div class="scanIdCardDiv">
<div class="scanCardContainer" v-show="afterTakingPhoto">
<video ref="video" id="video" :style="{width: divWidth}" autoplay></video>
<canvas ref="canvas" id="canvas" width="320" height="240" style="display: none;"></canvas>
</div>
</div>
</div>
<div class="takePhotoBtnDiv">
<div>
<button type="button" class="btn btn-info" #click="camera('environment')">Back Camera</button>
<button type="button" class="btn btn-info" #click="camera('user')">front Camera</button>
</div>
</div>
</div>
</template>
export default {
data() {
video: {},
front: true
},
methods: {
Camera() {
if(navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia({ video: { facingMode: (this.front? "user" : "environment") }}).then(stream => {
this.video.src = window.URL.createObjectURL(stream);
this.video.play();
});
}
},
changeCamera() {
this.front = !this.front;
}
},
mounted() {
this.Camera();
}
}
Can anyone help me out how do i change the camera? TIA
I got my solution. MediaDevices.getUserMedia() can't directly change video facingMode. First you have to stop the running video stream. And then change the video facingMode. Here is my code:
export default() {
data() {
},
methods: {
camera(face) {
this.stop();
this.gum(face);
},
stop() {
return video.srcObject && video.srcObject.getTracks().map(t => t.stop());
},
gum(face) {
if(face === 'user') {
return navigator.mediaDevices.getUserMedia({video: {facingMode: face}})
.then(stream => {
video.srcObject = stream;
this.localstream = stream;
});
}
if(face === 'environment') {
return navigator.mediaDevices.getUserMedia({video: {facingMode: {exact: face}}})
.then(stream => {
video.srcObject = stream;
this.localstream = stream;
});
}
}
},
mounted() {
this.camera('environment');
},
}

Vue-Dropzone processQueue not working

On my website you can upload a dog with attributes and images.
Vuejs is the frontend and Laravel the backend.
I am using this vue-dropzone component in my project to upload images.
The problem
I want to upload the images and the attributes of a dog at the same time (when the user clicks the submit button), so that the image files can be linked to the dog's id in the database.
Laravel function to register a new dog (route: 'api/dogs')
public function store(Request $request)
{
$attributes = [
'name' => $request->input('name'),
'type' => $request->input('dogType'),
...
];
$dogId = Dog::insertGetId($attributes);
// Upload files
if ($request->hasFile('files')) {
// getting all files
$files = $request->file('files');
// Count files to be uploaded
$file_count = count($files);
// start count how many uploaded
$uploadcount = 0;
if($uploadcount == $file_count) {
return true;
} else {
FileController::store($request, 0, 0, $dogId, $files, $uploadcount);
}
}
return $dogId;
}
Dropzone component (Formdropzone)
<template>
<div>
<dropzone
:id="this.id"
:url="this.url"
:accepted-file-types='"image/*"'
:use-font-awesome="true"
:preview-template="template"
:auto-process-queue="false" <----
:upload-multiple="true"
:parallel-uploads=100
:max-files=100
#vdropzone-success="showSuccess"
>
</dropzone>
</div>
</template>
<script>
import Dropzone from 'vue2-dropzone'
export default {
props: {
id: {
type: String,
required: true
},
url: {
type: String,
required: true
}
},
components: {
Dropzone
},
methods: {
showSuccess(file) {
console.log('A file was successfully uploaded')
},
template() {
return `
<div class="dz-preview dz-file-preview">
<div class="dz-image" style="width: 200px;height: 200px">
<img data-dz-thumbnail /></div>
<div class="dz-details">
<div class="dz-size"><span data-dz-size></span></div>
<div class="dz-filename"><span data-dz-name></span></div>
</div>
<div class="dz-progress"><span class="dz-upload" data-dz-uploadprogress></span></div>
<div class="dz-error-message"><span data-dz-errormessage></span></div>
<div class="dz-success-mark"><i class="fa fa-check"></i></div>
<div class="dz-error-mark"><i class="fa fa-close"></i></div>
</div>
`;
}
}
}
</script>
Register dog component
<tab-content title="Images">
<div class="form__input__wrapper">
<span class="label">Images (optional)</span>
<formdropzone url="http://domain.local/api/dogs" ref="dogDropzone" id="dogDropzone"></formdropzone>
</div>
</tab-content>
<script>
import Formdropzone from './Formdropzone'
export default {
data() {
return {
dog:{
name: '',
dogType: '',
...
}
}
},
methods: {
publish() {
this.$http.post('api/dogs', this.dog)
.then(response => {
this.$refs.dogDropzone.processQueue() <----
this.$router.push('/feed')
})
}
},
components: {
'formdropzone': Formdropzone
}
</script>
The error message
Uncaught (in promise) TypeError: Cannot read property 'processQueue' of undefined
I would be very thankful for any kind of help!