My Vue.js component is like this:
<template>
<div>
<div class="panel-group" v-for="item in list">
...
<div class="panel-body">
<a role="button" data-toggle="collapse" href="#purchase-{{ item.id }}" class="pull-right" aria-expanded="false" aria-controls="collapseOne">
Show
</a>
</div>
<div id="purchase-{{ item.id }}" class="table-responsive panel-collapse collapse" role="tabpanel">
...
</div>
</div>
</div>
</template>
<script>
export default {
...
computed: {
list: function() {
return this.$store.state.transaction.list
},
...
}
}
</script>
When executed, there exists an error like this:
Vue template syntax error:
id="purchase-{{ item.id }}": Interpolation inside attributes has
been removed. Use v-bind or the colon shorthand instead.
How can I solve it?
Use JavaScript code inside v-bind (or shortcut ":"):
:href="'#purchase-' + item.id"
and
:id="'purchase-' + item.id"
Or if using ES6 or later:
:id="`purchase-${item.id}`"
Use v-bind or shortcut syntax ':' to bind the attribute.
Example:
<input v-bind:placeholder="title">
<input :placeholder="title">
Just use
:src="`img/profile/${item.photo}`"
If you're pulling data from an array of objects, you need to include require('assets/path/image.jpeg') in your object like I did below.
Working example:
people: [
{
name: "Name",
description: "Your Description.",
closeup: require("../assets/something/absolute-black/image.jpeg"),
},
Using require(objectName.propName.urlPath) in the v-img element did not work for me.
<v-img :src="require(people.closeup.urlPath)"></v-img>
The easiest way is too require the file address:
<img v-bind:src="require('../image-address/' + image_name)" />
The complete example below shows ../assets/logo.png:
<template>
<img v-bind:src="require('../assets/' + img)" />
</template>
<script>
export default {
name: "component_name",
data: function() {
return {
img: "logo.png"
};
}
};
</script>
The most elegant solution is save images outside Webpack. By default, Webpack compress images in Base64, so if you save images in your assets folder, that doesn't work because Webpack will compress images in base64, and that isn’t a reactive variable.
To solve your problem, you need to save your images in your public path. Usually the public path is in "public" folder or "statics".
Finally, you can do this:
data(){
return {
image: 1,
publicPath: process.env.BASE_URL
}
}
And your HTML you can do this:
<img :src="publicPath+'../statics/img/p'+image+'.png'" alt="HANGOUT PHOTO">
When to use the public folder
You need a file with a specific name in the build output
File depends on a reactive variable that can change in execution time
You have images and need to dynamically reference their paths
Some library may be incompatible with Webpack and you have no other option but to include it as a <script> tag.
More information: "HTML and Static Assets" in Vue.js documentation
Related
Below is my file structure
Project
|-src
|-assets
|-images
----->|-logo.png
|-components
|-json
----->|-data.json
|-mainComp
----->|-exp.vue
Now here is my data.json code
"Experience": {
"0": {
"sectionTitle": "Awards",
"sectionContent": {
"0": {
"articleTitle": "Adobeedu",
"articleValue": "2019 Early Bird",
"articleDate": "Acheived on 2019",
"image": true,
"articleImgPath": "../../assets/images/logo.png",
"articleAlt": "AdobeEdu Early Bird Award"
}
}
}
}
and here below is the code of the exp.vue
<template>
<div>
<section class="exp__section" v-for="(data, index) in jsonTitle" :key="index">
<h5>{{data.sectionTitle}}</h5>
<article
v-for="(items, idx) in data.sectionContent"
v-bind:class="{'content__box':true, 'contains__image':(items.image === true)}"
:key="idx"
>
<h6>{{items.articleTitle}}</h6>
<div class="image__row">
<div class="image__box">
<!-- <img :src="items.articleImgPath" :alt="items.articleAlt" /> -->
</div>
<h3>{{items.articleValue}}</h3>
</div>
<p>{{items.articleDate}}</p>
</article>
</section>
</div>
</template>
<script>
import json from "#/components/json/english.json";
export default {
name: "ExperienceSection",
data() {
return {
jsonTitle: json.Experience
};
}
};
</script>
Now src does get the value: ../../assets/images/logo.png but the images don't load up. I thought maybe I am not accessing the file structure properly so I tried ./, ../, ../../../, ../../../../ but I think this may not be the problem, and I may need something to load the image after all.
It happens because Vue CLI uses Webpack to bundle the assets folder, and this bundling renames the files/path. So when binding to an asset source, the easiest way to overcome this is to use require in the template and hardcode the assets path and any subpath. For example
<img :src="require('#/assets/images/' + items.articleImgPath)" :alt="items.articleAlt">
Remove the path from the variable and only use the filename:
"articleImgPath": "logo.png",
This also keeps the JSON clean of path names.
I'm playing with Contentful! and I'm having trouble with Rich text content field.
I'm using '#contentful/rich-text-types' and #contentful/rich-text-html-renderer modules to customize the way this block is rendered and to display some assets and reference linked in Rich text content.
After calling getEntries in nuxt asyncData function, I've a description data available in my page component.
I'm using documentToHtmlString function with options.
Everything is working fine, but I would like to use a component I have already written (Post.vue), instead of returning the template in ES6 Template Strings.
I know that is possible, but I'm quite new to JS world.
I've tried to require components/post/Post.vue, but I don't know how to use it.
import { BLOCKS } from '#contentful/rich-text-types';
import { documentToHtmlString } from "#contentful/rich-text-html-renderer"
Vue component template where rich text field is rendered
<section class="container">
<div class="columns">
<div class="column">
<div v-html="formatContent(description)" />
</div>
</div>
</section>
I simply call formatContent method to call documentToHtmlString as follow (it works):
methods: {
formatContent(content) {
return documentToHtmlString(content, options)
}
}
And customize documentToHtmlString with options as described in doc:
const embeddedEntryRender = (node) => {
const { data: { target: entry} } = node
const fields = entry.fields
const sys = entry.sys
// LOOK HERE
// const postComponent = require('~/components/post/Post')
return `
<div class="column is-4">
<div class="card">
<div class="card-content">
<div class="media">
<div class="media-content">
<h3 class="title is-4">${fields.title}</h3>
<div class="subtitle is-6">${fields.description}</div>
</div>
</div>
<div class="content">
</div>
</div>
</div>
</div> `
}
const options = {
renderNode: {
[BLOCKS.EMBEDDED_ENTRY]: (node) => embeddedEntryRender(node),
// [BLOCKS.EMBEDDED_ASSET]: (node) => `<custom-component>${customComponentRenderer(node)}</custom-component>`
}
}
No errors detected
--
Thanks a lot
yep you can have a custom vue component in there with a different npm library, I had this same problem.
npm i contentful-rich-text-vue-renderer
in template:
<rich-text-renderer :document="document" :nodeRenderers="renderNode" />
where 'document' is the data sent form contentful, looks like your calling it description. RenderNode is a method described below.
in script:
data () {
return {
renderNode: [INLINES.ASSET_HYPERLINK]: (node, key, h) => {
return h('my-vue-component', { key: hey, props: { myProp: 'blah blah' }},'what I want inside the <my-vue-component> tag'`)
}
}
this might be kind of confusing. So First imprt the richTextRenderer component from that npm library and make sure to declare it in the components section of your vue component. (or gloablly)
Next pass into its 'document' prop the contentful rich text field
if you want custom rendering, pass into the nodeRenders prop a function (I had to declare it in the data section)
My example takes any asset hyperlink type and replaces it with a component of what I want inside the tag
I only got this to work if I globally declared the my-vue-component in the main.js file.
import MyVueComponent from 'wherever/it/is';
Vue.component('my-vue-component', MyVueComponent);
there are more configurations for this, just read the npm libs documentation (though its not great docs, it took my a long time to figure out how to pass props down, I had to read their github code to figure that out lol)
Say, I have the following single file component in Vue:
// Article.vue
<template>
<div>
<h1>{{title}}</h1>
<p>{{body}}</p>
</div>
</template>
After importing this component in another file, is it possible to get its template as a string?
import Article from './Article.vue'
const templateString = // Get the template-string of `Article` here.
Now templateString should contain:
<div>
<h1>{{title}}</h1>
<p>{{body}}</p>
</div>
It is not possible.
Under the hood, Vue compiles the templates into Virtual DOM render functions.
So your compiled component will have a render function, but no place to look at the string that was used to generate it.
Vue is not a string-based templating engine
However, if you used a string to specify your template, this.$options.template would contain the string.
set ref attribute for your component, and then you can get rendered HTML content of component by using this.$refs.ComponentRef.$el.outerHTML, and remember don't do this when created.
<template>
<div class="app">
<Article ref="article" />
</div>
</template>
<script>
import Article from './Article.vue'
export default {
name: 'App',
data() {
return {
templateString: ""
}
},
components: {
Article,
},
created() {
// wrong, $el is not exists then
// console.log(this.$refs.article.$el.outerHTML)
},
mounted() {
this.templateString = this.$refs.article.$el.outerHTML
},
}
</script>
My view is like this :
<div class="panel panel-default panel-store-info">
...
<div id="app">
<AddFavoriteProduct></AddFavoriteProduct>
</div>
....
</div>
My component is like this :
<template>
<a href="javascript:" class="btn btn-block btn-success" #click="addFavoriteProduct({{ $product->id }})">
<span class="fa fa-heart"></span> Favorite
</a>
</template>
<script>
export default{
name: 'AddFavoriteProduct',
props:['idProduct'],
methods:{
addFavoriteProduct(event){
event.target.disabled = true
const payload= {id_product: this.idProduct}
this.$store.dispatch('addFavoriteProduct', payload)
setTimeout(function () {
location.reload(true)
}, 1500);
}
}
}
</script>
When click button favorite, it will call controller on the laravel
I had register on the app.js like this :
...
import AddFavoriteProduct from './components/AddFavoriteProduct.vue';
...
components: {
...
AddFavoriteProduct
},
...
When executed, the button favorite does not appear.
Please help.
UPDATE
There exist error like this :
[Vue warn]: Unknown custom element: - did you register the component correctly? For recursive components, make sure to provide the "name" option. (found in root instance)
Whereas I had register it
Three fixes that I can see...
First, omit name: 'AddFavoriteProduct' in your component (it's not required for single-file components) and use kebab-case for the component in your template. Second, you appear to be missing the id-product prop
<add-favorite-product :id-product="someProductIdFromSomewhere"></<add-favorite-product>
Third, you don't use interpolation in bound properties and you don't even need to pass anything other than the $event to your addFavoriteProduct method
#click="addFavoriteProduct($event)"
HTML is case-insensitive, so your custom button element <AddFavoriteProduct></AddFavoriteProduct> is being interpreted as your Vue warning reports: <addfavoriteproduct>
When you name your component with camelCase or PascalCase, the corresponding tag name you should be using in your markup should be kebab-cased like so:
<add-favorite-product></add-favorite-product>
Vue knows to do the conversion from "dash-letter" to "uppercase-letter".
I am not used to vuex, as I generally implement my own store as a POJO object - not graduated with the learning curve of vuex. So can't provide a working solution with vuex, however to my knowledge, you need to kind of import the action(s) in your component - you can try the below
<template>
<a href="javascript:" class="btn btn-block btn-success" #click="addFavoriteProduct({{ $product->id }}, $event)">
<span class="fa fa-heart"></span> Favorite
</a>
</template>
<script>
export default{
props:['idProduct'],
vuex: {
actions: {
setFavoriteProduct: setFavoriteProduct
// assuming you named the action as setFavoriteProduct in vuex
// avoid duplicate naming for easy debugging
}
}
methods:{
addFavoriteProduct(productId, event){
event.target.disabled = true
const payload= {id_product: this.idProduct}
// this.$store.dispatch('addFavoriteProduct', payload)
this.setFavoriteProduct(payload);
setTimeout(function () {
location.reload(true)
}, 1500);
}
},
}
Note: From Vuex2 the store.dispatch() accepts only one argument (not a problem with your current use case), however if you need to pass multiple arguments to your action you can work around like
store.dispatch('setSplitData',[data[0], data [1]])
// in the action:
setSplitData (context, [data1, data2]) { // uses ES6 argument destructuring
OR
//... with an object:
store.dispatch('setSplitData',{
data1: data[0],
data2: data [1],
})
// in the action:
setSplitData (context, { data1, data2 }) { // uses ES6 argument destructuring
//source: LinusBorg's comment at https://github.com/vuejs/vuex/issues/366
Are you getting any error?
One mistake I see in your code is, you are taking one parameter: event, while you are trying to pass {{ $product->id }} to it, which seems a laravel variable. (Sorry, I don't know laravel)
If you want both event and product->id in method, you have to pass both parameters from HTML, like it is in docs with help of $event
<template>
<a href="javascript:" class="btn btn-block btn-success" #click="addFavoriteProduct({{ $product->id }}, $event)">
<span class="fa fa-heart"></span> Favorite
</a>
</template>
<script>
export default{
name: 'AddFavoriteProduct', // I am not sure if name is needed, don't remember seeing it in docs
props:['idProduct'],
methods:{
addFavoriteProduct(productId, event){
event.target.disabled = true
const payload= {id_product: this.idProduct}
this.$store.dispatch('addFavoriteProduct', payload)
setTimeout(function () {
location.reload(true)
}, 1500);
}
}
}
</script>
Another problem is you are expecting a prop idProduct, which is not being passed to component, You have to pass it like this in kebab-case:
<div class="panel panel-default panel-store-info">
...
<div id="app">
<AddFavoriteProduct id-product="4"></AddFavoriteProduct>
</div>
....
</div>
My Vue component contains some images. I want to do lazy-loading later, so I need to set the src of the images to a small image, first.
<template>
<div v-for="item in portfolioItems">
<a href="#{{ item.id }}">
<img
data-original="{{ item.img }}"
v-bind:src="/static/img/clear.gif"
class="lazy" alt="">
</a>
</div>
</template>
Gives me a bunch of errors, like:
[Vue warn]: Invalid expression. Generated function
body: /scope.static/scope.img/scope.clear.gif vue.common.js:1014[Vue
[Vue warn]: Error when evaluating expression "/static/img/clear.gif":
TypeError: Cannot read property 'call' of undefined (found in
component: )
webpack.config.js:
module.exports = {
// ...
build: {
assetsPublicPath: '/',
assetsSubDirectory: 'static'
}
}
This solution is for Vue-2 users:
In vue-2 if you don't like to keep your files in static folder (relevant info), or
In vue-2 & vue-cli-3 if you don't like to keep your files in public folder (static folder is renamed to public):
The simple solution is :)
<img src="#/assets/img/clear.gif" /> // just do this:
<img :src="require(`#/assets/img/clear.gif`)" // or do this:
<img :src="require(`#/assets/img/${imgURL}`)" // if pulling from: data() {return {imgURL: 'clear.gif'}}
If you like to keep your static images in static/assets/img or public/assets/img folder, then just do:
<img src="./assets/img/clear.gif" />
<img src="/assets/img/clear.gif" /> // in some case without dot ./
If you want to bind a string to the src attribute, you should wrap it on single quotes:
<img v-bind:src="'/static/img/clear.gif'">
<!-- or shorthand -->
<img :src="'/static/img/clear.gif'">
IMO you do not need to bind a string, you could use the simple way:
<img src="/static/img/clear.gif">
Check an example about the image preload here: http://codepen.io/pespantelis/pen/RWVZxL
This is how i solve it.:
items: [
{ title: 'Dashboard', icon: require('#/assets/icons/sidebar/dashboard.svg') },
{ title: 'Projects', icon: require('#/assets/icons/sidebar/projects.svg') },
{ title: 'Clients', icon: require('#/assets/icons/sidebar/clients.svg') },
],
And on the template part:
<img :src="item.icon" />
See it in action here
#Pantelis answer somehow steered me to a solution for a similar misunderstanding. A message board project I'm working on needs to show an optional image. I was having fits trying to get the src=imagefile to concatenate a fixed path and variable filename string until I saw the quirky use of "''" quotes :-)
<template id="symp-tmpl">
<div>
<div v-for="item in items" style="clear: both;">
<div v-if="(item.imagefile !== '[none]')">
<img v-bind:src="'/storage/userimages/' + item.imagefile">
</div>
sub: <span>#{{ item.subject }}</span>
<span v-if="(login == item.author)">[edit]</span>
<br>#{{ item.author }}
<br>msg: <span>#{{ item.message }}</span>
</div>
</div>
</template>
declare new variable that the value contain the path of image
const imgLink = require('../../assets/your-image.png')
then call the variable
export default {
name: 'onepage',
data(){
return{
img: imgLink,
}
}
}
bind that on html, this the example:
<img v-bind:src="img" alt="" class="logo">
hope it will help
You need use just simple code
<img alt="img" src="../assets/index.png" />
Do not forgot atribut alt in balise img
I had a similar issue with Vue where I tried to display several images by importing data from a configuration json file and then iterating over the data using v-for.
Even when I put require('../../assets/' + filename) right in the json, the images would never show. I eventually realized that Vue was interpreting my data value as a string, rather than a function. Good thing that javascript supports functions as a return type. So I made this function:
getImagePath(filename: string) {
return require('../../assets/' + filename);
}
I then just called that function from my v-for loop simply passing in the filenames from my config:
<v-list-item :key="place.id" v-for="place in placesOfPower">
<v-list-item-content class="justify-end">
<v-img :src="getImagePath(place.image)"
position="top center"
height="90"
width="30vw"/>
</v-list-item-content>
<v-list-item-content>
I found this thread on my search for a solution to show an image when it exists. I want to show a list of database entries that contain a type property. Each type should have a fitting png file in my vue assets folder. The whole list would break if a new type would be added without adding the image beforehand.
I found "Catch an error on requiring module in node.js" on stack overflow. The answer by Peter Lyons led me to my solution:
<template>
<v-data-table :items="items">
<template v-slot:item.type="{ item }">
<v-img
v-if="typeImageSource(item.type)"
:src="typeImageSource(item.type)"
/>
<template v-else>
{{ item.type }}
</template>
</template>
</v-data-table>
</template>
<script>
export default {
data () {
return {
// In reality this gets filled from a db:
items: [
{ id: 1, type: 'abc' },
{ id: 2, type: 'abcd' },
{ id: 3, type: 'efg' },
]
}
},
methods: {
typeImageSource: function (type) {
let src = ''
try {
src = require(`#/assets/types/${('' + type).toLowerCase()}.png`)
} catch (error) {
console.warn(`Image for type ${type} could not be found! Please add "${('' + type).toLowerCase()}.png" to the folder "#/assets/types/".\n\n`, error)
return null
}
return src
},
},
}
</script>
If you are using nuxt
use <img :src="'_nuxt/path_to_your_local_image'" />
if you are using vue
first use static src import : <img src="path_to_your_local_image" />
then inspect image element to see what src is rendered to the browser
then replace it with a dynamic src