Change the CSS if dynamically changing image is loaded in Vue 3 - vue.js

I'm trying to change the CSS class of a div based on if an image is loaded. The image URL is taken from an <input> field, so <img src> can change. I've managed to set the CSS class on the first page-load using the #load event. But if I change the image's URL in the input field to a non-existent image, then the CSS doesn't change. How do I track if the input's value has changed and "re-check" if the image is loaded?
In the below example, I want to have green-bg if the image exists and red-bg if the image doesn't exist.
<div id="app">
<div :class="imgLoaded ? 'green-bg' : 'red-bg'">
<img :src=imgURL #load="imgLoaded = true" />
<br/>
<input v-model="imgURL" />
</div>
</div>
<script>
export default {
data() {
return {
imgURL: 'https://picsum.photos/200/300',
imgLoaded: false,
};
}
};
</script>
Link to CodePen

This is a nice place to use a watcher.
watch: {
imgURL(newVal, oldVal) {
if (newVal !== oldVal) this.imgLoaded = false
}
}
Hooking to the input event would work too, but there is an edge case where the new value is similar to the old value. If you then set the imgLoaded to false, the CSS class won't change to green-bg because the load event does not fire (since the url did not change).
When using a watcher you can compare the old value to the new value and only set imgLoaded to false if the values are different.
Here is the pen.

Add to you input:
<input type="text" v-model="imgURL" #input="updateURL">
and create function on methodth section:
updateURL() {
this.imgLoaded = false
}

I managed to solve my issue by using the #error method. imgLoaded would be set to false on error and set to true on load.
<div id="app">
<div :class="imgLoaded ? 'green-bg' : 'red-bg'">
<img :src=imgURL #load="imgLoaded = true" #error="imgLoaded = false" />
<br/>
<input v-model="imgURL" />
</div>
</div>
This works for the case where the CSS class is changed based on whether the image exists or not.

Related

Interpolating Data Values with the use of V-If & V-Else?

I have a paragraph in my app, in which the user can translate it at the click of a button. I interpolate the text seen via my props (props.row.text) and my data (this.data.translatedText).
The data property is empty/null until the button is cliked, which therefore causes me to receive the error "data is null" in my Console. I attempt to fix this with the code below, however, this throws me error but does not display the interpolated text after the button click.
Does anyone have a suggestion for how best to structure this, so that the {{ props.row.text }} is replaced by {{ this.data.translatedText }} after the button is clicked?
Component.vue
<span id="translationButton">
<translation-button #click="calltranslatedText()" #changeTitle="ChangeT" />
</span>
<div class="ticket-text-container">{{ props.row.text }}</div>
<div class="ticket-text-container" v-if="calltranslatedText()">{{ this.data.translatedText }}</div>
methods: {
ChangeT(title)
{
this.translatedText = title
},
calltranslatedText() {
return true
},
Firstly don't use this and data in the template just indicate a prop name you defined in data function.
Secondly, you can just use translatedText in v-if to show div with the translated text.
<div class="ticket-text-container" v-if="translatedText">{{ translatedText }}</div>
Don't forget to set translatedText to an empty string or null to hide this block again if needed.

ie11 caching background images while changing styles

I have a DIV which holds background images, and I change them by bootstrap carousel item change by passing data-bg attribute like below:
HTML
<div class="bg-holder" data-bg="bg-1">
<div id="carousel">
<div class="carousel-item active" data-bg="bg-1">
.... some slider content
</div>
<div class="carousel-item" data-bg="bg-2">
.... some slider content
</div>
</div>
</div>
JS
const bgHolder= document.querySelector(".bg-holder");
$('#carousel').on('slide.bs.carousel', function (e) {
bgHolder.dataset.bg = e.relatedTarget.dataset.bg;
})
CSS
.bg-holder[data-bg="bg-1"] {
background-image: url(image1.jpg)
}
.bg-holder[data-bg="bg-2"] {
background-image: url(image2.jpg)
}
I set data-bg="bg-1" by default and then on every carousel change i pass the new data-bg value. it works great in all modern browsers except IE11 which do not refresh the images from css, and it keeps displaying the one i loaded by default. When I open developers tools and uncheck/check the declaration it displays the proper image. Any ideas ?
I test in IE and reproduce the issue. As a workaround, you could set the background-image as inline style of bg-holder and change it on every carousel change according to bg value. The sample code is like below:
HTML:
<div class="bg-holder" style="background-image: url(image1.jpg)">
<div id="carousel">
<div class="carousel-item active" data-bg="bg-1">
.... some slider content
</div>
<div class="carousel-item" data-bg="bg-2">
.... some slider content
</div>
</div>
</div>
JS:
$('#carousel').on('slide.bs.carousel', function (e) {
if (e.relatedTarget.dataset.bg == "bg-2") {
$(".bg-holder").css("background-image", "url(image2.jpg)");
}
else {
$(".bg-holder").css("background-image", "url(image1.jpg)");
}
})
You could also check this online demo in IE 11.

How do I fit the value of one v-model into another v-model?

In this example, I'm trying to fit the value from div id="message" into textarea using the Vue v-model construct, but this not work
<template>
<div>
<textarea v-model="text"></textarea>
</div>
<div>
<div id="message" v-model="text2">{{ comment.message }}</div>
<button #click="update(text2);">
Edit
</button>
</div>
</template>
<script>
export default {
data() {
return {
text: [],
text2: null
}
},
methods: {
/* not work */
update(text2) {
this.text = text2;
}
}
<script>
How do I make sure that when I click on the "edit" button, the value of v-model="text2" insert into v-model="text" ?
You cannot use v-model on a <div> because it isn't an input element.
It seems what you want to do is set text to the comment message when you click the edit button so that it can be edited by the textarea. All you have to do is pass comment.message as the argument:
<button #click="update(comment.message)">
A couple of other things:
You cannot have multiple root elements in your template (you have two root <div> elements). You can just wrap everything in a single <div>.
text has initial value [] which isn't compatible with a textarea's v-model; did you mean ''?

How to pass an image src into a modal dialog in Vue (in order to zoom the clicked image)?

What I'm trying to do is to zoom an image on click, the idea is:
you click on an image
dialog of width=85vw opens up with the image you just clicked on inside of it (so the image is displayed almost fullscreen now)
I cannot think of a better way of "zooming" an image on click, but to open it in a modal dialog (if there's an easier way, please let me know).
Code:
<v-dialog v-model="dialog" max-width="85vw" >
<img :src="img1" alt="" width="100%" #click.stop="dialog=false">
</v-dialog>
<img :src="img1" width="500px" #click.stop="dialog = true">
<img :src="img2" width="500px" #click.stop="dialog = true">
<img :src="img3" width="500px" #click.stop="dialog = true">
export default {
data() {
img1: "../../src/assets/pexels-photo-373912.jpg",
img2: "../../src/assets/pexels-photo-373912.jpg",
img3: "../../src/assets/pexels-photo-373912.jpg"
}
}
The problem is, it's not opening any clicked image in a dialog, just the one you hard coded in there, in this example it will always open img1 no matter what image you click.
I don't know how to pass the :src into the dialog dynamically - the :src of the image you clicked.
P.S. v-dialog is a component from Vuetify.js library
Question:
Is there an obviously better way of doing it?
If not really, how do I make this method to work and display the image I clicked in the modal dialog?
You need a variable to hold which image is selected. When you click on an image, it should set the variable to the url for that image. When you click on the dialog image, it should unset the variable.
The dialog should show when the variable is set and otherwise be hidden.
For simplicity, I'm not using an actual dialog, just a div. It looks like you would use it for the dialog's v-model rather than using the v-if I use.
new Vue({
el: '#app',
data: {
selectedImage: null,
images: [
'http://via.placeholder.com/400x300?text=image%201',
'http://via.placeholder.com/600x400?text=image%202',
'http://via.placeholder.com/500x500?text=image%203'
]
},
methods: {
zoom(url) {
console.log("Zoom", url);
this.selectedImage = url;
}
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
<div v-if="selectedImage" max-width="85vw">
<img :src="selectedImage" alt="" width="100%" #click.stop="selectedImage = null">
<hr>
</div>
<div v-for="url in images">
<img :src="url" width="100px" #click="zoom(url)">
</div>
</div>
to click distributed images across the page define a property and change it with click event
in template
<v-dialog v-model="dialog" max-width="60%" #keydown.esc="cancel">
<v-card>
<v-img :src="pic" alt="" contain/>
</v-card>
</v-dialog>
<v-img
src="require(#/assets/clinic/1.jpeg)"
alt=""
contain
#click="openPic(require('#/assets/clinic/1.jpeg'))"//this part is important you have to require image
/>
in script
data() {
return {
pic: "",
dialog: false
}
},
methods: {
openPic(image) {
this.dialog = true
this.pic = image
},
cancel() {
this.dialog = false
}
}

label not moving with Vuejs2 + Materializejs

I have a problem with the label of a text input with materializejs + vuejs
the label does not move up if I change the value of the input field with vue (it behaves correctly if I change manually the input)
a simple (not elegant) code showing that is (also on JSfiddle):
correct expected behavior by editing the field manually
not correct behavior by clicking on the last div (starting from an empty field): it changes the value of the input without moving the label
html
<div id=app>
<div class="input-field ">
<input type="text" v-model="value" id="field">
<label for="field">Field</label>
</div>
<div #click="value='newValue'">
clickOnMe
</div>
</div>
Javascript
new Vue({
el: '#app',
data: {
value: ''
}
})
See Prefilling Text Inputs section, seems there's a patch function that needs to be applied
<div #click="clickMe">
methods: {
clickMe() {
this.value = 'newValue';
$(document).ready(function() {
Materialize.updateTextFields();
});
}
}