Execute code after rendering all images in vuejs - javascript

I want to do some code execution after all images are loaded (need the set scroll in a specific position). I use nextTik() but Codes are processed before loading images . We also can't use mounted and created and methods like this because codes should be executed after clicking on a button.
is there any method or trick?
thanks

You can use the load event on each image to check if all images have been loaded.
example: https://jsfiddle.net/jacobgoh101/e5bd86k6/1/
<div id="app">
<img #load="handleLoad" #error="handleLoad" v-for="i in 10" :src="`https://picsum.photos/${Math.random()*300}/700/?random`"/>
</div>
Javascript
new Vue({
el: "#app",
data: {
imgLoaded: 0
},
methods: {
handleLoad: function(todo){
this.imgLoaded++;
if(this.imgLoaded === 10) {
alert('all image loaded')
}
}
}
})

Based on ( https://stackoverflow.com/a/50481269 enter link description here) answer I did something like this:
<img class="card-img-top" :src=" 'images/' + (data.src || 'image-default.png') "
:alt="data.alt" #load="handleImagesLoad" #error="handleImagesLoad">
(#load and #error on each image on my page) and
handleImagesLoad: function($event){
var images = $('img:not([loaded])');
var target = $($event.target);
$(target).attr('loaded', true);
if(images.length <= 1){
$('#preloader').fadeOut();
}
},

Here is my approach for a component having both "static" images and an an arbitray number of images fetched from database :
Append two items to the component data: 'imgsLength' and 'loadedImgsLength'
Append a 'load' event handler to every img element, that increments 'loadedImgsLength'
After all fetched data are loaded, set 'imgsLength' to the number of all img elements
So, you know that all images are loaded when 'loadedImgsLength' > 0 and 'loadedImgsLength' == 'imgsLength'
In this exemple, my component won't be visible until all images are loaded:
<template>
<div v-show="ready" ref="myComponent">
...
<img src="..." #load="loadedImgsLength++" /> (static image)
...
<img v-for="img in fetchedImg" #load="loadedImgsLength++" /> (dynamic images)
</div>
</template>
<script>
...
data(){
return {
...,
imgsLength : 0,
loadedImgsLength: 0,
...,
}
},
computed: {
ready(){
return (this.loadedImgsLength > 0 && this.loadedImgsLength == this.imgsLength)
}
},
async created(){
... fetch all data
this.$nextTick(() => this.$nextTick(this.imgsLength = this.$refs.myComponent.getElementsByTagName("img").length))
}

Related

Previously added image in the dom start showing the spinner when new image is added

I have this simple app where I am showing images and also can add more. So when I add an image I show a spinner while it loads and then hide the spinner when its done loading. But the issue is when I'm adding the second image, the spinner doesn't show up. This is because of the boolean I'm using and I think it is being used globally.
Please note that I add an image through image url and rendering it with img html tag of course. I'm using RactiveJS and I feel like there is something I can do with an unique identifier but just dont know how. All the images have unique id attached to its data.
Here's the Ractive code:
let isImageLoaded = false;
const ractive = new Ractive({
el: '#container',
template: `
{{#each images}}
<div class="container">
{{#if !isImageLoaded}}
<span class="spinner"></span>
{{/if}}
<img
alt=""
src="{{url}}"
on-load="imageLoaded"
style="display: {{isImageLoaded ? 'block' : 'none'}};"
/>
</div>
{{/each}}
`,
data: {
images: [
{ id: 1, url: 'https://www.some-image.com/image1.jpg' },
],
isImageLoaded : isImageLoaded
}
});
ractive.on('imageLoaded', function(e) {
ractive.set('isImageLoaded', true);
});
function addImage(image) {
const allImages = ractive.get('images');
allImages.push(images);
ractive.set('isImageLoaded', false);
ractive.set('images', allImages);
}
If I set isImageLoaded to false in addImage function then adding a new image makes all the other spinners to show up.
How can I use ids to make each image and spinner unique and show spinner only when adding a new image?

Changing image onclick then changing it back in js

fiddle:
https://jsfiddle.net/0r7v923u/2/
<img src="https://dirask.com/static/bucket/1631898942509-VMYrnXyYZv--image.png" class="logo" alt="Banner" onclick="ratesD(this)" />
JS:
function ratesD(image) {
if (img.attr('src') == "https://dirask.com/static/bucket/1631898942509-VMYrnXyYZv--image.png") {
image.src = "https://dirask.com/static/bucket/1633375165831-yjQ7G6WQeL--image.png";
} else {
image.src = "https://dirask.com/static/bucket/1631898942509-VMYrnXyYZv--image.png"
}
}
I am simply trying to change the image back and forth on click. The function below changes it but it does not return to the previous image:
function ratesD(image) {
image.src = 'https://dirask.com/static/bucket/1633375165831-yjQ7G6WQeL--image.png';
}
I thought it only needs to change using img.attr('src') == what do I need to change for the if condition?
First you are trying to access the wrong property of the image object (attr instead of src) and the second function is not checking the current image source before changing it. To fix this, the function should check the current src of the image and change it to the other URL depending on its value. Try this.
function ratesD(image) {
if (image.src == "https://dirask.com/static/bucket/1631898942509-VMYrnXyYZv--image.png") {
image.src = "https://dirask.com/static/bucket/1633375165831-yjQ7G6WQeL--image.png";
} else {
image.src = "https://dirask.com/static/bucket/1631898942509-VMYrnXyYZv--image.png"
}
}
<img src="https://dirask.com/static/bucket/1631898942509-VMYrnXyYZv--image.png" class="logo" alt="Banner" onclick="ratesD(this)" />
It's a bad idea to force load/unload your images (even if they are in the system cache) every time you click on them.
Load them only once, and switch their display at each click.
const bannerImgs = document.querySelector('#banner-images');
bannerImgs.onclick =_=> bannerImgs.classList.toggle('seeOther');
#banner-images > img {
width : 100px;
height : 100px;
}
#banner-images.seeOther > img:first-of-type,
#banner-images:not(.seeOther) > img:last-of-type {
display : none;
}
<div id="banner-images" >
<img src="https://dirask.com/static/bucket/1631898942509-VMYrnXyYZv--image.png" alt="Banner" >
<img src="https://dirask.com/static/bucket/1633375165831-yjQ7G6WQeL--image.png" alt="Banner" >
</div>

how to Set display none in print pages based on condition in vue.js using css or js?

I am working in vue framework , i am working currently to print the webpages for my application. I need solution for one condition. I have addressed it below.
<template>
<div id = "intro" style = "text-align:center;">
<div
v-for="report in reports"
:key="report.property"
:class="['Section1', { 'non-print-css' : noprint }]">
<div class="Icon" id="printReport_AId" v-if="isOverview">
<font-awesome-icon :icon="report.icon" #click="printWindow()"/>
</div>
</div>
<div class="logo"> this text should be none when printing . if the section1 is none in screen. </div>
</div>
</template>
<script type = "text/javascript">
var vue_det = new Vue({
el: '#intro',
data: {
timestamp: ''
},
created() {
},
methods: {
printWindow() {
const curURL = document.location.href;
history.replaceState(history.state, '', '/');
const originalTitle = document.title;
document.title = '. ';
window.print();
document.title = originalTitle;
history.replaceState(history.state, '', curURL);
},
computed: {
noprint() {
const printicon = document.getElementById('printReport_AId');
if (printicon.style.display === 'none') {
return true;
}
return false;
},
}
}
});
</script>
<style>
.non-print-css {
// i have to set the display none for logo when its printed.
}
</style>
I have two div class
section1
logo.
condition:
If section 1 display none then when i print the page, the logo
should not be printed.
I have added condition in computed to check if the section display is
none if it is true it should execute the css where we set #print.
logo display is none.
I need a proper solution either in css or in js
You can apply print specific styling using media queries, and specifically
#media print {
.non-print-css {
display: none;
}
}
"If section 1 has display none" ... means that the element is rendered but not shown you have to give your section1 a v-show directive not a v-if ... which when set to false (v-show) your element will have display : none.
" then when i print the page, the logo should not be printed"... so if section1 v-show is set to false we are not going to print right .... on this case we should create a variable in our data and bind that to section1 (v-show) so when that variable is false then the element has display : none so we don't print .... lets call that variable isSection1 ...now on our method only if isSection1 is true then we print ... here how it should be :
data: {
isSection1: false, // false means section1 has display : none
timestamp: ''
},
methods: {
printWindow() {
// only if section1 doesn't have display : none
if (this.isSection1) {
const curURL = document.location.href;
history.replaceState(history.state, '', '/');
const originalTitle = document.title;
document.title = '. ';
window.print();
document.title = originalTitle;
history.replaceState(history.state, '', curURL);
}
}
}
<div class="section1" v-show="isSection1"> </div>
I passed through here looking for a way to conditionally render a component but keep the space when it's not been rendered.
After passing from here I found this awesome publication that explains 3 options available in VueJS.
So, here you are enter link description here
I have fixed it .partially but the element id is null , the event is not listened
Add the v-bind to logo class.
Add mounted() and computed () as below.
mounted() {
window.addEventListener('print', this.noprint);
},
computed(){
noprint() {
const printicon = document.getElementById('printReport_AId');
if (printicon != 'null') {
return true;
}
return false;
},
},

Vue js - Set alt image when image source not found

I am working on a site which has a list of products. Each product has a corresponding image. I am binding the image url to the source attribute like below.
<img :src="product.ImageUrl"/>
If the image is not found, I want to show a default image.
I do as below in cshtml razor syntax (for reference only)
onerror='this.onerror = null;this.src = "#Url.Content("~/images/photo-not-available.jpg")";'
How do I achieve the same in Vue?
You can set an onerror handler function with #error in Vue:
<img :src="" #error="aVueFunctionThatChangesTheSrc">
Since in pure js put onerror inline like this
<img :src="ImgUrl" onerror="this.src='http://example.com/default.jpg'"/>
for vue.js we can replace it
<img :src="ImgUrl" #error="$event.target.src='http://example.com/default.jpg'"/>
I found that changing the src in the #error function kicked off a horrible endless loop of updates, causing a flickering screen etc. My solution so far is:
<span v-if="props.column.field === 'avatar'">
<span v-if="props.row.avatar">
<img alt="avatar" class="round-32" :src="`${props.row.avatar}`" #error="(() => props.row.avatar = null)"/>
</span>
<span v-else>
<img alt="avatar" class="round-32" src="../../../assets/images/avatar-2.jpg"/>
</span>
</span>
I have try to using #error but it doesn't work because in my case, the image was not found, so I try this one, and its work.
<img :src="getImgUrl(filename)">
methods: {
getImgUrl(filename){
try{
return require(`#/assets/${filename}`)
}catch(_){
return require(`#/assets/default.png`)
}
},
}
I ended up with a directive to set a fallback image and prevent the loop:
image-src-fallback.js
const setSrc = (evt) => {
const el = evt.currentTarget;
el.setAttribute("src", el.fallback);
el.fallback = "";
el.removeEventListener("error", _listener);
};
const _listener = (evt) => setSrc(evt);
export default {
bind(el, binding) {
el.fallback = binding.value;
el.addEventListener("error", _listener);
},
unbind(el) {
el.removeEventListener("error", _listener);
},
};
Global import
import imageSrcFallback from "./directives/image-src-fallback.js";
Vue.directive("src-fallback", imageSrcFallback);
Use
<img
v-src-fallback="user.avatar_fallback_url"
:src="user.avatar_url"
/>
Sometimes you can create a method, for example in my case some images are not found because clients have not uploaded images, then you can create a method for it: you can return any value as an image or simple '' for any image.
<div class="flex-shrink-0">
<img
class="h-72 w-full object-cover"
:src="showFirstImageGallery(p.project_images)"
/>
</div>
methods: {
showFirstImageGallery(v){
if (v.length > 1) {
return v[1].img_url
} else {
return ''
}
}
},

Infinite update loop

I'm trying to build a reusable Image Loader component in Vue.js, which should:
Manage its own thumbnail data
Take src from parent as prop
Display different thumbnails based on prop, using same instance without being destroyed
So it may take data from two places (own thumbnail state || src prop), and I have a very difficult time wrapping my head around how to manage this. Not too sure if this is the right approach to the problem either.
At this point I am getting an infinite update loop warning in the console.
[Vue warn]: You may have an infinite update loop in a component render function.
Here is my code. Any help whatsoever would be greatly appreciated.
<template>
<div>
<label class="fileContainer">
<span class="icon is-large">
<i class="fa fa-camera"></i>
</span>
<input type="file" :index="index" #change="updateThumbnail"/>
</label>
<object
:data="pdfURL"
type="application/pdf"
:class="{visible: pdfURL != ''}">
</object>
<img :src="getSrc()" />
</div>
</template>
<script>
export default {
props: ["index", "srcProp"],
data() {
return {
imageSrc: '',
imageDataURI: '',
pdfURL: '',
}
},
methods: {
getSrc() {
if (typeof this.srcProp !== "undefined") {
this.imageSrc = ''
if (this.srcProp !== '') {
this.imageSrc = this.srcProp
} else {
this.imageSrc = this.imageDataURI
}
} else {
this.imageSrc = this.imageDataURI
}
return this.imageSrc
},
updateThumbnail(event) {
this.$emit('change')
const fileTypes = ['jpg', 'jpeg', 'png']
const imgFile = event.target.files[0] || event.srcElement.files[0]
const extension = imgFile.name.split('.').pop().toLowerCase()
const isImg = fileTypes.indexOf(extension) > -1
if (extension === 'pdf') {
const pdfURL = URL.createObjectURL(imgFile);
this.pdfURL = pdfURL
this.height = 200
return
} else if (isImg) {
var reader = new FileReader();
reader.readAsDataURL(imgFile);
reader.onload = () => {
this.imageDataURI = reader.result
return
}
} else {
alert("Please submit images or PDFs only.")
}
},
}
}
</script>
I was facing the same problem. Let me explain to you what I know.
When :src="anyFunction()" is used it re-renders upto infinite time even after it gets the result.
At this point we get same array for infinite times. Try displaying console.log('this.imgSrc'), you will get infinite number of array.
Here we cannot use slice or splice to get the first array. I haven't found solution but I managed to keep variables in a src rather than rendering whole function and getting url.
<img :src="'https://maps.googleapis.com/maps/api/staticmap?zoom=15&size=500x250&markers=color:red%7Clabel:L%7C'+this.leadIpinfo.loc.split(',').slice(0,1)+','+this.Ipinfo.loc.split(',').slice(1,2) alt="loc">
Here I have fetched the array and splited and sliced into 2 values.
Hope it could help in some ways.

Categories

Resources