Image in VueJS component is not loading - javascript

I got a parent component which sends a data object to the children component like this:
<child object-data=" url: 'url here', title: 'Title'"></child>
Then on my children component, I get this object data by doing:
<script>
export default {
props: [
'objectData'
]
}
</script>
Now, for some reason, I can use title without any problem like this {{ objectData.title }} and shows up.
But when it comes to the URL inside an img tag it's not rendering the image.
I attempted doing the following:
<img :src="objectData.url"/> <--- not rendering
<img v-bind:src="objectData.url"/> <--- not rendering
<img v-bind:src="require(objectData.url)"/> <-- throws a warning error because it's not a path but an object I guess.
<img v-bind:src="{objectData.url}"/> <--- throws an error
<img v-bind:src="{{objectData.url}}"/> <--- throws an error
When I check on the dom element, it doesn't even contain a src attribute afterwards.
If I write down the URL without an object, it works.
<img v-bind:src="src/assets/images/icon.png"/>
But I want my URL to come from a parent component.
Any ideas on how to solve this? I added the url-loader on my webpack file:
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader'
}
I also attempted returning objectData.url from a computed/methods fuction:
computed: {
getImageUrl: function() {
return objectData.url;
}
}
And then use it like :src=“getImageUrl” or :src=“{{getImageUrl}}” and I wasn’t lucky either.

I faced the same issue and i fixed it by using require function :
in the parent component App.vue :
<carousel-posts :posts="posts" />
export default {
name: "app",
data() {
return {
posts: [
{
img: require("./assets/logo.png"),
title: "Title 1",
subTitle: "Sub Title 1",
body:"lorem ipsum ..."
}
...
]
};
}
...
}
in the child component i loop through the posts and bind the image src as follows :
<div class="card-body" v-for="(post,idx) in posts" >
<img class="card-img" :src="post.img" />
...
</div>
<script>
export default {
props: ["posts"],
...
So in your case you should have something like :
<child :object-data="objectData"></child>
...
data(){
return{
dataObject:{
url: require('./assets/someimg.png'),
title: 'Title'
}
}
}
Update :
my project tree :
src
|_assets
| |_logo.png
|_components
| |_CarouselPosts.vue
|_App.vue

Two alternatives:
Static links that vue resolves. But they're src-based and don't work with webpack / file-loader:
<img :src="'../assets/filename.png'"/>
Works with webpack. Note the ".default" at the end:
<img :src="require('../assets/filename.png').default"/>
Documentation relevant effective Nov 2019:
https://github.com/vuejs/vue-loader/issues/1612

Related

Vue js. Images from list not displaying properly [duplicate]

I got a parent component which sends a data object to the children component like this:
<child object-data=" url: 'url here', title: 'Title'"></child>
Then on my children component, I get this object data by doing:
<script>
export default {
props: [
'objectData'
]
}
</script>
Now, for some reason, I can use title without any problem like this {{ objectData.title }} and shows up.
But when it comes to the URL inside an img tag it's not rendering the image.
I attempted doing the following:
<img :src="objectData.url"/> <--- not rendering
<img v-bind:src="objectData.url"/> <--- not rendering
<img v-bind:src="require(objectData.url)"/> <-- throws a warning error because it's not a path but an object I guess.
<img v-bind:src="{objectData.url}"/> <--- throws an error
<img v-bind:src="{{objectData.url}}"/> <--- throws an error
When I check on the dom element, it doesn't even contain a src attribute afterwards.
If I write down the URL without an object, it works.
<img v-bind:src="src/assets/images/icon.png"/>
But I want my URL to come from a parent component.
Any ideas on how to solve this? I added the url-loader on my webpack file:
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader'
}
I also attempted returning objectData.url from a computed/methods fuction:
computed: {
getImageUrl: function() {
return objectData.url;
}
}
And then use it like :src=“getImageUrl” or :src=“{{getImageUrl}}” and I wasn’t lucky either.
I faced the same issue and i fixed it by using require function :
in the parent component App.vue :
<carousel-posts :posts="posts" />
export default {
name: "app",
data() {
return {
posts: [
{
img: require("./assets/logo.png"),
title: "Title 1",
subTitle: "Sub Title 1",
body:"lorem ipsum ..."
}
...
]
};
}
...
}
in the child component i loop through the posts and bind the image src as follows :
<div class="card-body" v-for="(post,idx) in posts" >
<img class="card-img" :src="post.img" />
...
</div>
<script>
export default {
props: ["posts"],
...
So in your case you should have something like :
<child :object-data="objectData"></child>
...
data(){
return{
dataObject:{
url: require('./assets/someimg.png'),
title: 'Title'
}
}
}
Update :
my project tree :
src
|_assets
| |_logo.png
|_components
| |_CarouselPosts.vue
|_App.vue
Two alternatives:
Static links that vue resolves. But they're src-based and don't work with webpack / file-loader:
<img :src="'../assets/filename.png'"/>
Works with webpack. Note the ".default" at the end:
<img :src="require('../assets/filename.png').default"/>
Documentation relevant effective Nov 2019:
https://github.com/vuejs/vue-loader/issues/1612

How to get a src image via js?

I want to get the src image and bind it to my img tag, I'd create it in JS file and I imported, but nothing happened, first file is About.vue and the second one is my utils file
<template>
<div>
<Header></Header>
<main>
<section v-if="this.showAlert">
<b-alert show variant="success">
<h4 class="alert-heading">Well done!</h4>
<p>
This web-app was made by Gabriel Nunes, he did it all by himself.
All these buttons and headers that you saw on the app, was made by
him too.
</p>
<hr />
<p class="mb-0">
If you need anything from him, please contact via social netorks.
Social networks:
<img
:src="this.showImg.facebook.src"
:width="this.showImg.facebook.width"
:height="this.showImg.facebook.height"
/>
</p>
<p class="mb-1">
<img
:src="this.showImg.github.src"
:width="this.showImg.github.width"
:height="this.showImg.github.height"
/>
</p>
</b-alert>
</section>
<section v-if="!this.showAlert">
<b-alert show variant="secondary">
<h4 class="alert-heading">Well done!</h4>
<p>
Aww yeah, you successfully read this important alert message. This
example text is going to run a bit longer so that you can see how
spacing within an alert works with this kind of content.
</p>
<hr />
<p class="mb-0">
Whenever you need to, be sure to use margin utilities to keep things
nice and tidy.
</p>
</b-alert>
</section>
</main>
</div>
</template>
<script>
import Header from "./Header.vue";
import { pathImgObj } from "../../utils.js";
export default {
components: {
Header,
},
data() {
return {
showImg: {},
showAlert: Boolean,
};
},
created() {
this.showAlert = confirm("Are you sure about this?");
this.showImg = { ...pathImgObj };
},
methods: {},
};
</script>
<style>
</style>
Utils file js
export const config = {
home: 'Home',
anime: 'Animes',
faq: 'FAQs',
about: 'About'
}
export const pathImgObj = {
facebook: {
src: `./assets/img/facebook.png`,
width: '30px',
height: '30px'
},
github: {
src: `./assets/img/github.png`,
height: '30px',
width: '30px'
}
}
Expected result: get the src, width and height from utils file and pass it to my About.vue
How can I do this?
My results:
When a URL is required or imported, Webpack processes the URL, and decides what to do with it. For image URLs, Webpack finds the image, transforms it (e.g., image scaling and optimization), puts the result in an output directory, and returns the result's URL (e.g., dist/img/foo.abcd123.jpg).
vue-loader automatically requires only static URLs of <img>.src:
<img src="#/assets/foo.jpg"> ✅
<img :src="'#/assets/' + imgUrl1"> ❌
<img :src="imgUrl2"> ❌
export default {
data() {
return {
imgUrl1: 'foo.jpg',
imgUrl2: '#/assets/foo.jpg',
}
}
}
Dynamically bound <img>.src must be manually required either in the binding value or in the <script> block:
<img :src="require('#/assets/' + imgUrl1)"> ✅
<img :src="require(imgUrl2)"> ✅
<img :src="imgUrl3"> ✅
export default {
data() {
return {
imgUrl1: 'foo.jpg',
imgUrl2: '#/assets/foo.jpg',
imgUrl3: require('#/assets/foo.jpg'),
}
}
}
Since the URLs in the utils file are dynamically bound in the template, vue-loader does not require them, and thus they're treated as static assets in public/. For example, a static URL of ./assets/foo.jpg refers to public/assets/foo.jpg.

How to check if image is loaded in Vue/Nuxt?

New to Vue and Nuxt. I am trying to show skeletons before image is loaded completely.
Here's my attempt, skeleton shows but image is never loaded and onImageLoad is never called.
<template>
<div>
<img v-if="isLoaded" #load="onImgLoad" :src="me.img">
<div v-else class="skeleton"></div>
</div>
</template>
<script lang="ts">
export default {
props: {
me: Object,
},
data() {
return {
isLoaded: false,
}
},
methods: {
onImgLoad() {
console.log(` >> isLoaded:`, this.isLoaded)
return this.isLoaded = true
},
},
}
</script>
I have some broken image url to test fallback src, is it a problem? But I tried removing those broken links and it's still not working.
Example data:
export const me = {
name: 'David',
img: 'https//david.png', // Example broken > https://no.jpg
},
Please help me, what I am doing wrong?
Because isLoaded is false on the initial render, the img element is removed from the DOM tree. If it’s not in the DOM, the image src won’t be requested, ergo— no load event.
Switch to v-show. The img element will remain in the DOM, so the #load event will fire.

How do i dynamically generate vue components from wordpress custom fields

I am building a website using vue with nuxt, loading data from a wordress site through the rest api.
I would like to give the client the ability to modify page templates using custom fields, so I need to dynamically create my vue templates with vue components that are generated depending on the custom fields placed in the wordpress page editor.
This is simplified, but for example, if the clients builds a page with three custom fields:
[custom-field type='hero']
[custom-field type='slider']
[custom-field type='testimonial']
I can get the field information via the rest api in a json object, like this:
page: {
acf: [
{field 1: {
{type: 'hero'},
{content: '...'}
},
{field 2: {
{type:'slider'},
{content: '...'}
},
{field 3: {
{type:'testimonial'},
{content: '...'}
}
}
}
I'll bring this into my vue app, but then i would the template to generate dynamically from a list of possible components mapped to the custom field types. the above would output:
<template>
<Hero ... />
<Slider ... />
<Testimonial ... />
</template>
Would this be done using the v-is directive (https://v2.vuejs.org/v2/guide/components-dynamic-async.html) like:
<component v-for="field in custom-fields" v-is="field.type" :data="field.data"/>?
Is this possible? Any help would be greatly appreciated.
You can dynamically register and display components in Nuxt but there are some caveats.
Method 1 - SSR & SSG Support
This method will register your components dynamically and maintain the integrity of Server Side Rendering/Static Site Generation.
The only trade-off here is that you will need to list the names and file locations of all potential imported components. This shouldn't be too cumbersome for your use-case (I don't imagine you have too many ACF fields) but it could be a lengthy job if you're intending to build an entire component library from it.
<template>
<div>
<div v-for="field in page.acf" :key="field.uniqueId">
<component :is="field.type" :my-prop="field.content" />
</div>
</div>
</template>
<script>
export default {
components: {
hero: () => import('~/components/hero.vue'),
slider: () => import('~/components/slider.vue'),
testimonial: () => import('~/components/testimonial.vue')
},
data() {
return {
page: {
acf: [
{
uniqueId: 1,
type: 'hero',
content: '...'
},
{
uniqueId: 2,
type: 'slider',
content: '...'
},
{
uniqueId: 3,
type: 'testimonial',
content: '...'
}
]
}
}
}
}
</script>
Method 2 - Client Side Only Rendering
This method allows you to programmatically register the component's name and file location. This saves you from writing out each component one by one, but comes at the cost of no SSR or SSG support. However, this may be preferable if you're going the SPA route.
<template>
<div>
<div v-for="field in page.acf" :key="field.uniqueId">
<no-ssr>
<component :is="field.type" :my-prop="field.content" />
</no-ssr>
</div>
</div>
</template>
<script>
import Vue from 'vue'
export default {
data() {
return {
page: {
acf: [
{
uniqueId: 1,
type: 'hero',
content: '...'
},
{
uniqueId: 2,
type: 'slider',
content: '...'
},
{
uniqueId: 3,
type: 'testimonial',
content: '...'
}
]
}
}
},
mounted() {
const sections = this.page.acf
for (let i = 0; i < sections.length; i++) {
Vue.component(sections[i].type, () =>
import(`~/components/${sections[i].type}.vue`)
)
}
}
}
</script>
Be aware that the <no-ssr> tag is being deprecated and if you are using Nuxt above v2.9.0, you should use <client-only> instead.
Notes On Your Question
I understand you've tried to simplify your data architecture but your JSON object would be rather difficult to loop through since the key changes on each object in array. You've also got unnecessary objects in the data structure. I've simplified the structure in the data method so you can understand the concept.
To get the v-for loop working, you need to nest the component inside a HTML tag that has a v-for attribute (as demonstrated above).
You may want to sanitise the data you get from the WordPress API by ensuring that WordPress doesn't supply you with a module that doesn't correspond to a component. If the API supplies a type that doesn't correspond to a component, the whole build will fail.
Hope this helps!
P.S If anyone knows of a method that allows you to programatically set the component name and component file location while maintaining SSG - please let me know!

Vue JS - Problem with computed property not updating

I am quite new with VueJS and I have been having trouble lately with some computed properties which do not update as I would like. I've done quite some research on Stack Overflow, Vue documentation and other ressources but i haven't found any solution yet.
The "app" is basic. I've got a parent component (Laundry) which has 3 child components (LaundryMachine). The idea is to have for each machine a button which displays its availability and updates the latter when clicked on.
In order to store the availability of all machines, I have a data in the parent component (availabilities) which is an array of booleans. Each element corresponds to a machine's availability.
When I click on the button, I know the array availibities updates correctly thanks to the console.log. However, for each machine, the computed property "available" does not update is I would want it to and I have no clue why.
Here is the code
Parent component:
<div id="machines">
<laundry-machine
name="AA"
v-bind:machineNum="0"
v-bind:availableArray="this.availabilities"
v-on:change-avlb="editAvailabilities"
></laundry-machine>
<laundry-machine
name="BB"
v-bind:machineNum="1"
v-bind:availableArray="this.availabilities"
v-on:change-avlb="editAvailabilities"
></laundry-machine>
<laundry-machine
name="CC"
v-bind:machineNum="2"
v-bind:availableArray="this.availabilities"
v-on:change-avlb="editAvailabilities"
></laundry-machine>
</div>
</div>
</template>
<script>
import LaundryMachine from './LaundryMachine.vue';
export default {
name: 'Laundry',
components: {
'laundry-machine': LaundryMachine
},
data: function() {
return {
availabilities: [true, true, true]
};
},
methods: {
editAvailabilities(index) {
this.availabilities[index] = !this.availabilities[index];
console.log(this.availabilities);
}
}
};
</script>
Child component:
<template>
<div class="about">
<h2>{{ name }}</h2>
<img src="../assets/washing_machine.png" /><br />
<v-btn color="primary" v-on:click="changeAvailability">
{{ this.availability }}</v-btn>
</div>
</template>
<script>
export default {
name: 'LaundryMachine',
props: {
name: String,
machineNum: Number,
availableArray: Array
},
methods: {
changeAvailability: function(event) {
this.$emit('change-avlb', this.machineNum);
console.log(this.availableArray);
console.log('available' + this.available);
}
},
computed: {
available: function() {
return this.availableArray[this.machineNum];
},
availability: function() {
if (this.available) {
return 'disponible';
} else {
return 'indisponible';
}
}
}
};
</script>
Anyway, thanks in advance !
Your problem comes not from the computed properties in the children, rather from the editAvailabilities method in the parent.
The problem is this line in particular:
this.availabilities[index] = !this.availabilities[index];
As you can read here, Vue has problems tracking changes when you modify an array by index.
Instead, you should do:
this.$set(this.availabilities, index, !this.availabilities[index]);
To switch the value at that index and let Vue track that change.

Categories

Resources