v-img load dynamic images raise eror [Vuetify] Image load failed - javascript

In my case, I have two unrelated components. The first component has a button. The second component has <v-img> element of the Vuetify framework. The version of Vuetify which I use is 2.2.4. When the user clicks the button I pass some paraments by EventBus. I want to change the image in the second component after that click. For some reason in console I see that error: [Vuetify] Image load failed. eager property don't work. I also changed v-img to img. The result is the same. The image doesn't change. How to solve this problem for your opinion?
The first component (button click event logic):
EventBus.$emit('showLegend', {
imageName: 'http://domain/logo.png',
legendVisible: true
})
The second component:
<template>
<v-img
eager
v-if="legendVisible"
:src="legendURL">
</v-img>
</template>
<script>
import { EventBus } from '../../services/events.js'
export default {
data () {
return {
legendURL: 'http://domain/logo.png',
legendVisible: true
}
},
created () {
EventBus.$on('showLegend', data => {
if (data) {
this.legendURL = data.imageName
this.legendVisible = data.legendVisible
}
})
}
}
</script>

So you are saying that the image is bound correctly and displays, but the issue is with the change in URL after the emit via EventBus happened? If it had not worked in the first place, I'd have assumed you might need something related to require. Many times for static images that's the issue. You'd have to write
<v-img src="require('http://domain/logo.png')"></v-img>
However, I could not get this syntax to work with my own dynamic variables coming from a v-for="card in cards" myself until I used
<v-img :src="require('#/assets/' + card.imgSrc + '.jpg')"></v-img>
which I find rather dissatisfying.

Related

How to verify modal window is displayed or not

I'm a newbie of Vuejs.
In vue2, I already have verified whether component is displayed by specific trigger.
However, I cannot verify whether above displayed component is disappeared after another trigger.
I tried to verify wrapper.findComponent('...').exists() is false.
However, I got true when component should be disappeared.
At first, I suspect another trigger does not work well,
so I called wrapper.html(), and there is no component that I want to verify.
That's why, trigger works well, probably.
My question is, as I said at title, How to verify component existance when flag is toggled.
Below is my code.
code for test.
test('After date pick, picker is closed', async() => {
let node = document.createElement('body')
const wrapper = mount(App, {
localVue,
vuetify,
attachTo: node
})
// modal window does not appear yet.
expect(wrapper.findComponent('.v-picker').exists()).toBe(false)
// `enable` is set to true. Then, modal window is displayed.
// and verification got true
const menu = wrapper.getComponent(DateMenu)
await menu.setData({ enable: true })
expect(wrapper.findComponent('.v-picker').exists()).toBe(true)
// $emit input event cause `enable` set to false
const picker = wrapper.getComponent('.v-picker')
await picker.vm.$emit('input', '2020-01-01')
// html() returns result in which there is no elements related to toggled component
console.log(wrapper.html())
expect(menu.vm.$data.enable).toBe(false)
// test fail findComponent found a component
expect(wrapper.findComponent('.v-picker').exists()).toBe(false)
})
As reference, source code (short version).
<template>
<!-- v-model="enable" controls modal window is displayed or not-->
<v-menu v-model="enable">
<template v-slot:activator="{ on, attrs }">
<v-text-field
v-model="date"
v-bind="attrs"
v-on="on"
>
</v-text-field>
</template>
<!-- This is displayed and verified existance in test. -->
<v-date-picker v-model="date" #input="input"> </v-date-picker>
</v-menu>
</template>
<script>
export default {
data: () => {
return {
// enable controls modal window is displayed or not.
// true: displayed
// false: disappear
enable: false,
};
},
methods: {
input(date) {
this.enable = false
}
}
};
</script>
I think in your test, you encounter an update applied by Vue, which is asynchronous behavior.
So after changing the reactive property (enable = false), you should wait for Vue to update DOM by adding this line:
await Vue.nextTick();
expect(wrapper.findComponent('.v-picker').exists()).toBe(false);
Vuetify renders its dialogs, menus, pickers (etc.) outside of the application wrapper, that's why wrapper.html() doesn't help you.
I believe the correct way is to use attachTo option, so you can mount your component inside the div with the data-app attribute.
If that doesn't work for you, you can also set data-app attribute on body:
let node = document.createElement('body');
node.setAttribute("data-app", true);

Vue.JS - listen to click on component

I'm fairly new to Vue.JS and currently having an issue listening to a click event on a component.
JS:
Vue.component('photo-option', {
data: function () {
return {
count: 0
}
},
props: ['src'],
template: `
<img :src=src v-on:click="$emit('my-event')" />
`
});
HTML:
<photo-option :src=uri v-for='uri in aboutUsPhotos' v-on:my-event="foo" />
...where foo is a method on my main Vue instance.
The above is based on the Vue.JS docs for handling component events, and I can't see what I'm doing wrong. The alert doesn't fire, and there's no errors in the console.
Before I found those docs, I also tried simply adding v-on:click='...' to both the JS (i.e. the template) and the HTML, each with no success.
What am I doing wrong?
[EDIT]
This is happening because the code is picked up by a lightbox script and has its DOM position changed. So presumably the binding/event attachment is being lost.
Does Vue have any way of allowing for this, perhaps by 'reinitialising' itself on an element, or something?

Responding to a click on a Vue element programmatically

Essentially, I want my Vue instance to respond to a click on an uploaded thumbnail.
I'm using the FineUploader Vue package with the template layout per the docs (see end of the question). Upon uploading an image, a tree like this is outputted:
<Root>
<Gallery>
<Thumbnail>
</Gallery>
</Root>
Coming from a jQuery background I really have no idea about the 'correct' way to go about this given that the Thumbnail Template is defined by the package already, and so I'm not creating my own Thumbnail template. I know that I can access elements like this:
let thumb = this.$el.querySelector('.vue-fine-uploader-thumbnail');
And perhaps a listener
thumb.addEventListener('click', function() {
alert('I got clicked');
});
But dealing with the Vue instance being re-rendered etc. I'm not familiar with.
Vue Template:
<template>
<Gallery :uploader="uploader" />
</template>
<script>
import FineUploaderTraditional from 'fine-uploader-wrappers'
import Gallery from 'vue-fineuploader/gallery'
export default {
components: {
Gallery
},
data () {
const uploader = new FineUploaderTraditional({
options: {/*snip*/}
})
return {
uploader
}
}
}
</script>
In order to respond to click events you add the v-on:click (or it's short form: #click) to whatever tag you want.
If you have elements that are nested that respond to the click event you might experience that a click on a child triggers a parents click event. To prevent this you add #click.stop instead, so that it doesn't trigger the parents click.
So you would have something along the lines of:
<Gallery #click="myFunction" />

How do I correctly use my inherited datas from my parent component?

I'm a newbie with VueJS and I'm setting up a rather simple website where the user can change some elements from the website, such as the website's background image.
From the admin part of the website, the user can upload an image that will be used as one of the page's background. The value is stored in a Rails API, and, when a user gets to the visitor's part, VueJS calls the settings from Rails, then displays it where I want them. I use Vue-Router, so I make the call for the settings in my App.vue and transmit the received datas to the <router-view> component. I decided to do so to load all the datas only once instead of loading them for each page change.
Here is how it looks:
App.vue
<template>
<div id="app">
<transition :name="transitionName">
<router-view :settings="settings" :me="bio"></router-view>
</transition>
</div>
</template>
<script>
export default {
data: function () {
return {
settings: null
}
},
created () {
this.$http.secured.get('/settings')
.then(response => {
this.settings = response.data
})
.catch(error => this.setError(error))
}
}
</script>
front/Landing.vue
<template>
<div id="landing" :style="{background: this.settings.landing_bg.url}">
<p>{{ this.settings.landing_bg.url }}</p>
<!-- Some other elements, not related -->
</div>
</template>
<script>
export default {
props: ['settings'],
created () {
console.log(this.settings)
}
}
</script>
<style scoped>
</style>
I have several problems with this configuration:
The first one is that I get errors saying that VueJS can't read "landing_bg" of null
But VueJS has no problem displaying the image's path in the <p> right under it
console.log(this.settings) returns null when I reload the page, but will display the settings correctly if I go to another page then back. However, no background image is set.
I tried to declare with datas() what structure this.settings will have, but VueJS tells me that it doesn't like how there are two settings declared.
I guess this is a problem with asynchronous loads, but how should I handle that? Should I use VueX?
Thank you in advance
First of all, this is redundant in your :style, change it to:
:style="{ background: settings.landing_bg.url }"
By the way, unless your landing_bg.url looks like this: url('bg_url') you might want to create a computed property:
computed: {
bgURL() { return `background: url('${this.settings.landing_bg.url}')` }
}
You might also have to add a v-if in that div to render it only after settings have been loaded. That error pops up because settings is still null when the component is created.
<div id="landing" :style="bgURL" v-if="settings">
I tried to declare with datas() what structure this.settings will have, but VueJS tells me that it doesn't like how there are two settings declared.
No, declare it on the props, like this
props: {
'settings': {
type: Object,
default: () => ({
landing_bg: {
url: '' //here you can specify a default URL or leave it blank to just remove the error
}
})
},
},

Creating a global modal using react-redux and foundation reveal

I'm creating a modal that displays information about a specific user and it's triggered by clicking on a picture. The modal should be able to fire up from different components. My approach is as follows (I use React with Redux):
create store with a "modal" property that's set to a reducer "modalReducer"
modal: modalReducer
modal is an object with the following properties: showModal=false, user=undefined, image=undefined
When a div containing picture is clicked, the action is being dispatched that sets showModal to true (with username and url image link as arguments).
export var openModal = (user, image) => {
return {
type: 'OPEN_MODAL',
user,
image
}
}
For the actual modal I use a foundation reveal modal and in its render method I check if modal.showModal == true and use jQuery to open it up.
Now, toggling the modal works as charm but I cannot use the modal.user and modal.image properties inside of the render method as it prints the following error message:
Uncaught Error: findComponentRoot(..., .0.2.0.0.1): Unable to find element. This probably means the DOM was unexpectedly mutated (e.g., by the browser), usually due to forgetting a when using tables, nesting tags like , , or , or using non-SVG elements in an parent. Try inspecting the child nodes of the element with React ID ``.
I'm almost certain the problem lies within the ProfileModal.jsx component. Any idea what I'm doing wrong?
export class ProfileModal extends React.Component {
componentDidMount () {
var elem = new Foundation.Reveal($('#profile-modal'));
$('#profile-modal').on('closed.zf.reveal',()=>{this.props.dispatch(actions.hideModal())});
}
render () {
var {modal} = this.props;
if (modal.showModal) {
$('#profile-modal').foundation('open');
}
return(
<div>
<div id="profile-modal" className="reveal large">
<div className="modal-container">
<div className="modal-picture" style={{backgroundImage: `url(${modal.image})`}}></div>
<p>{modal.user}</p>
...

Categories

Resources