Vue bind url not functioning properly with image src - vue.js

<template>
<div id="app">
<b-container >
<b-row>
<div class="product">
<img src='./assets/SocksG.jpg' alt="">
</div>
<div class="cart">
<button #click="addToCart">Add To Cart</button>
<br>
<p>Cart({{cart}})</p>
</div>
<div v-for="variant in variants"
:key="variant.variantId"
class="color-box"
:style="{backgroundColor:variant.variantColor}"
#mouseover ="updateProduct(variant.variantImage)"
>
</div>
</b-row>
</b-container>
</div>
</template>
export default {
name: 'App',
data() {
return {
cart: 0,
variants: [{
variantId: 2234,
variantColor: 'green',
variantImage: './assets/SocksG.jpg'
}, {
variantId: 2235,
variantColor: 'blue',
variantImage: './assets/SocksB.jpg'
}]
}
},
methods: {
addToCart() {
this.cart += 1
},
updateProduct(variantImage) {
this.image = variantImage
}
}
}
At first I applied :src to the socksG image and in the script I had to use img:require("./assets/SocksG.jpg" and it worked.
Now with the #mouseover event handler that's supposed to activate the updateProduct() function, I get the feeling that I'm doing something wrong with the Vue url handler that should fetch the variantImage(s) because when I use the Vue devtools the image url is img/SocksG.jpg and not ./assets/SocksG.jpg. What am I doing wrong?

Add a data property for image:
return {
image: ..., // Set some initial image filename here
cart: 0,
...
}
Use require like this:
<img :src="require('#/assets/' + this.image)" alt="">
and remove the paths from the urls:
variantImage: 'SocksG.jpg'
...
variantImage: 'SocksB.jpg'
Now the image src is bound to this.image, which you are changing with the mouseover.

Related

Change src from img, but only the one who was clicked in VueJS

I'd like to change the src from the specific image clicked only, but it's changing all images. All the solutions I could find show elements in loop, which is not the case here:
<div id="app">
<div>
<img :src="icon" #click="change" />
</div>
<div>
<img :src="icon" #click="change" />
</div>
<div>
<img :src="icon" #click="change" />
</div>
</div>
const app = Vue.createApp({
data() {
return {
icon: 'index.png'
}
},
methods: {
change() {
this.icon = 'icon.png'
}
}
})
app.mount('#app')
The reason they all update at once is that they all get their src value from the same property. If you're wanting to change them individually, they'll all need their own properties and modifications to the change method so it knows what to update.
Try doing something like this:
<template>
<img :src="icon1" #click="change('icon1')" />
<img :src="icon2" #click="change('icon2')" />
<img :src="icon3" #click="change('icon3')" />
</template>
<script>
export default {
name: "App",
data() {
return {
icon1: "index.png",
icon2: "index.png",
icon3: "index.png",
};
},
methods: {
change(prop) {
this[prop] = "icon.png";
},
},
};
</script>
I created a simple codesandbox that should show it in action.

Removing specific object from array keeps removing last item

Here is what I have and I will explain it as much as I can:
I have a modal inside my HTML code as shown below:
<div id="favorites-modal-edit" class="modal">
<div class="modal-background"></div>
<div class="modal-card px-4">
<header class="modal-card-head">
<p class="modal-card-title">Favorites</p>
<button class="delete" aria-label="close"></button>
</header>
<section class="modal-card-body">
<div class="container">
<div id="favorites-modal-edit-wrapper" class="columns is-multiline buttons">
<favorites-edit-component v-for="(favorite, index) in favorites_list" :key="favorite.id" :favorite="favorite" />
</div>
</div>
</section>
<footer class="modal-card-foot">
<button class="button" #click="addItem">
Add Item
</button>
</footer>
</div>
</div>
The id="favorites-modal-edit" is the Vue.js app, then I have the <favorites-edit-component /> vue.js component.
Here is the JS code that I have:
I have my favorites_list generated which is an array of objects as shown below:
const favorites_list = [
{
id: 1,
name: 'Horse',
url: 'www.example.com',
},
{
id: 2,
name: 'Sheep',
url: 'www.example2.com',
},
{
id: 3,
name: 'Octopus',
url: 'www.example2.com',
},
{
id: 4,
name: 'Deer',
url: 'www.example2.com',
},
{
id: 5,
name: 'Hamster',
url: 'www.example2.com',
},
];
Then, I have my vue.js component, which is the favorites-edit-component that takes in the #click="removeItem(this.index) which is coming back as undefined on the index.
Vue.component('favorites-edit-component', {
template: `
<div class="column is-half">
<button class="button is-fullwidth is-danger is-outlined mb-0">
<span>{{ favorite.name }}</span>
<span class="icon is-small favorite-delete" #click="removeItem(this.index)">
<i class="fas fa-times"></i>
</span>
</button>
</div>
`,
props: {
favorite: Object
},
methods: {
removeItem: function(index) {
this.$parent.removeItem(index);
},
}
});
Then I have the vue.js app that is the parent as shown below:
new Vue({
el: '#favorites-modal-edit',
// Return the data in a function instead of a single object
data: function() {
return {
favorites_list
};
},
methods: {
addItem: function() {
console.log('Added item');
},
removeItem: function(index) {
console.log(index);
console.log(this.favorites_list);
this.favorites_list.splice(this.favorites_list.indexOf(index), 1);
},
},
});
The problem:
For some reason, each time I go to delete a item from the list, it's deleting the last item in the list and I don't know why it's doing it, check out what is happening:
This is the guide that I am following:
How to remove an item from an array in Vue.js
The item keeps coming back as undefined each time the remoteItem() function is triggered as shown below:
All help is appreciated!
There is an error in your favorites-edit-component template, actually in vue template, when you want to use prop, data, computed, mehods,..., dont't use this
=> there is an error here: #click="removeItem(this.index)"
=> in addition, where is index declared ? data ? prop ?
you're calling this.$parent.removeItem(index); then in removeItem you're doing this.favorites_list.splice(this.favorites_list.indexOf(index), 1); this means that you want to remove the value equal to index in you array no the value positioned at the index
=> this.favorites_list[index] != this.favorites_list[this.favorites_list.indexOf(index)]
In addition, I would suggest you to modify the favorites-edit-component component to use event so it can be more reusable:
favorites-edit-component:
<template>
<div class="column is-half">
<button class="button is-fullwidth is-danger is-outlined mb-0">
<span>{{ favorite.name }}</span>
<span class="icon is-small favorite-delete" #click="$emit('removeItem', favorite.id)">
<i class="fas fa-times"></i>
</span>
</button>
</div>
</template>
and in the parent component:
<template>
...
<div id="favorites-modal-edit-wrapper" class="columns is-multiline buttons">
<favorites-edit-component
v-for="favorite in favorites_list"
:key="favorite.id"
:favorite="favorite"
#removeItem="removeItem($event)"
/>
</div>
...
</template>
<script>
export default {
data: function () {
return {
favorites_list: [],
};
},
methods: {
...
removeItem(id) {
this.favorites_list = this.favorites_list.filter((favorite) => favorite.id !== id);
}
...
},
};
I would restructure your code a bit.
In your favorites-edit-component
change your removeItem method to be
removeItem() {
this.$emit('delete');
},
Then, where you are using your component (in the template of the parent)
Add an event catcher to catch the emitted "delete" event from the child.
<favorites-edit-component v-for="(favorite, index) in favorites_list" :key="favorite.id" :favorite="favorite" #delete="removeItem(index)"/>
The problem you have right now, is that you are trying to refer to "this.index" inside your child component, but the child component does not know what index it is being rendered as, unless you specifically pass it down to the child as a prop.
Also, if you pass the index down as a prop, you must refer to it as "index" and not "this.index" while in the template.

How do you pass data in the router-view

Like for example
<router-link :to="{ name : 'criminalView', params : { criminalId : this.criminal.id , criminal : this.criminal } }" tag="a" >
<img class="h-18 w-18 rounded-full mr-4 mt-2" src="{{ asset('assets/images/'.$criminal->photo) }}" id="criminalsPhoto" alt="Criminals View" >
</router-link>
how can i accept those params in my CriminalView.vue which handles the router-view component
This is my routes.js
import VueRouter from 'vue-router';
import CriminalView from './components/CriminalView.vue';
import GroupView from './components/GroupView.vue';
let routes = [
{
path : '/criminal/:criminalId',
name : 'criminalView',
component : CriminalView,
props : { criminals }
},
{
path : '/group/:groupId',
name : 'groupView',
component : GroupView,
},
]
export default new VueRouter({
routes,
linkActiveClass: 'is-active'
});
how am I gonna display this in my template such as like this
<section class="w-2/5 ml-2 font-basic" id="criminalProfile">
<p class="font-basic tracking-normal text-2xl mb-4 mt-4 font-normal text-black mr-2">Criminal Profile of {{ this.criminal.full_name }}</p>
<div class="bg-white px-8 py-8 pt-4">
<div class="text-center">
<div id="avatar" class="inline-block mb-6" >
<img :src="avatarPath" class="h-50 w-50 rounded-full border-orange border-2">
<p class="font-bold mt-2 text-blue">$15,000</p>
<p class="mt-2 text-lg font-bold" >Notable Crimes:
<p class="mt-2 text-lg font-normal" >
<em class="font-bold roman">Offenses</em>Descr..
</p>
</p>
<div class="w-full flex justify-between">
<button class="w-full bg-green-theme p-3 text-white mt-4 ml-2 hover:bg-green-second" href="/criminal/" >View Full Profile</button> </div>
</div>
</div>
</div>
</section>
This is in my script tag..
export default {
props : ['criminals'],
name: 'CriminalProfile',
data(){
return {
criminal : this.criminals,
url : window.App.apiDomain
}
},
}
How can i display the props in my router-view which is there in my CriminalView.vue
I'm not 100% sure about camelCasing for groupId so I'm using groupid. Try it out, and let us know about camelCase in the comments.
props: {
//you could set the watch to this if you need.
//groupid: {default: 1}
},
watch: {
$route(to, from) {
var groupid = (typeof to.params.groupid != 'undefined') ? to.params.groupid : 1;
//... do some stuff
}
},
In order to display data from the route params you should be doing something like this
// start route.js
export default new Router({
mode: 'hash', // https://router.vuejs.org/api/#mode
routes: [
{
path: 'my-route/:criminal',
name: 'MyRoute',
component: MyComponent
}
]
})
// End route.js
<template>
<section>
<div>
{{criminals}}
</div>
</section>
</template>
<script>
export default {
name: 'CriminalProfile',
data(){
return {
criminals: ''
}
},
created(){
this.criminals = this.$route.query.myparam
}
}
</script>
Here it's the router link
<router-link :to="{ name: 'CriminalView', params: { criminalId: 123 }}">Criminal</router-link>
In my case this work perfectly,
Try this:
Using this.$route.params.paramName you can access params in router-view component.
<script>
export default{
data() {
criminal_id:'',
criminal_data: ''
},
created() {
this.criminal_id = this.$route.params.criminalId;
this.criminal_data = this.$route.params.criminal;
}
}
</script>

Send data from one component to another in vue

Hi I'm trying to send data from one component to another but not sure how to approach it.
I've got one component that loops through an array of items and displays them. Then I have another component that contains a form/input and this should submit the data to the array in the other component.
I'm not sure on what I should be doing to send the date to the other component any help would be great.
Component to loop through items
<template>
<div class="container-flex">
<div class="entries">
<div class="entries__header">
<div class="entries__header__title">
<p>Name</p>
</div>
</div>
<div class="entries__content">
<ul class="entries__content__list">
<li v-for="entry in entries">
{{ entry.name }}
</li>
</ul>
</div>
<add-entry />
</div>
</div>
</template>
<script>
import addEntry from '#/components/add-entry.vue'
export default {
name: 'entry-list',
components: {
addEntry
},
data: function() {
return {
entries: [
{
name: 'Paul'
},
{
name: 'Barry'
},
{
name: 'Craig'
},
{
name: 'Zoe'
}
]
}
}
}
</script>
Component for adding / sending data
<template>
<div
class="entry-add"
v-bind:class="{ 'entry-add--open': addEntryIsOpen }">
<input
type="text"
name="addEntry"
#keyup.enter="addEntries"
v-model="newEntries">
</input>
<button #click="addEntries">Add Entries</button>
<div
class="entry-add__btn"
v-on:click="openAddEntry">
<span>+</span>
</div>
</div>
</template>
<script>
export default {
name: 'add-entry',
data: function() {
return {
addEntryIsOpen: false,
newEntries: ''
}
},
methods: {
addEntries: function() {
this.entries.push(this.newEntries);
this.newEntries = '';
},
openAddEntry() {
this.addEntryIsOpen = !this.addEntryIsOpen;
}
}
}
</script>
Sync the property between the 2:
<add-entry :entries.sync="entries"/>
Add it as a prop to the add-entry component:
props: ['entries']
Then do a shallow merge of the 2 and emit it back to the parent:
this.$emit('entries:update', [].concat(this.entries, this.newEntries))
(This was a comment but became to big :D)
Is there a way to pass in the key of name? The entry gets added but doesn't display because im looping and outputting {{ entry.name }}
That's happening probably because when you pass "complex objects" through parameters, the embed objects/collections are being seen as observable objects, even if you sync the properties, when the component is mounted, only loads first level data, in your case, the objects inside the array, this is performance friendly but sometimes a bit annoying, you have two options, the first one is to declare a computed property which returns the property passed from the parent controller, or secondly (dirty and ugly but works) is to JSON.stringify the collection passed and then JSON.parse to convert it back to an object without the observable properties.
Hope this helps you in any way.
Cheers.
So with help from #Ohgodwhy I managed to get it working. I'm not sure if it's the right way but it does seem to work without errors. Please add a better solution if there is one and I'll mark that as the answer.
I follow what Ohmygod said but the this.$emit('entries:update', [].concat(this.entries, this.newEntries)) didn't work. Well I never even need to add it.
This is my add-entry.vue component
<template>
<div
class="add-entry"
v-bind:class="{ 'add-entry--open': addEntryIsOpen }">
<input
class="add-entry__input"
type="text"
name="addEntry"
placeholder="Add Entry"
#keyup.enter="addEntries"
v-model="newEntries"
/>
<button
class="add-entry__btn"
#click="addEntries">Add</button>
</div>
</template>
<script>
export default {
name: 'add-entry',
props: ['entries'],
data: function() {
return {
addEntryIsOpen: false,
newEntries: ''
}
},
methods: {
addEntries: function() {
this.entries.push({name:this.newEntries});
this.newEntries = '';
}
}
}
</script>
And my list-entries.vue component
<template>
<div class="container-flex">
<div class="wrapper">
<div class="entries">
<div class="entries__header">
<div class="entries__header__title">
<p>Competition Entries</p>
</div>
<div class="entries__header__search">
<input
type="text"
name="Search"
class="input input--search"
placeholder="Search..."
v-model="search">
</div>
</div>
<div class="entries__content">
<ul class="entries__content__list">
<li v-for="entry in filteredEntries">
{{ entry.name }}
</li>
</ul>
</div>
<add-entry :entries.sync="entries"/>
</div>
</div>
</div>
</template>
<script>
import addEntry from '#/components/add-entry.vue'
import pickWinner from '#/components/pick-winner.vue'
export default {
name: 'entry-list',
components: {
addEntry,
pickWinner
},
data: function() {
return {
search: '',
entries: [
{
name: 'Geoff'
},
{
name: 'Stu'
},
{
name: 'Craig'
},
{
name: 'Mark'
},
{
name: 'Zoe'
}
]
}
},
computed: {
filteredEntries() {
if(this.search === '') return this.entries
return this.entries.filter(entry => {
return entry.name.toLowerCase().includes(this.search.toLowerCase())
})
}
}
}
</script>

How to Add Vue Component as an HTML Tag

I'm new to using VueJS, and I'm working on a learning project right now.
I have a component called "Draggable", and another one called "ModalPage".
"ModalPage" is as follows:
The code for this page is below:
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<button #click="propagate">Propagate</button>
<h3>Vue Modals</h3>
<ul id="list">
</ul>
<!-- <div v-html="template"></div> -->
</div>
</template>
<script>
import draggable from '#/components/Draggable.vue';
export default {
name: 'ModelPage',
components: {
draggable,
},
data () {
return {
msg: 'Welcome to the Modal Popup Page',
template: `<draggable></draggale>`,
}
},
methods: {
propagate () {
// console.log("propagated")
list.append(`<draggable></draggale>`)
}
}
}
</script>
I also have a component called "Draggable" and its code is as follows:
<template>
<div id="app">
<VueDragResize :isActive="true" :w="200" :h="200" v-on:resizing="resize" v-on:dragging="resize">
<h3>Hello World!</h3>
<p>{{ top }} х {{ left }} </p>
<p>{{ width }} х {{ height }}</p>
</VueDragResize>
</div>
</template>
<script>
import VueDragResize from 'vue-drag-resize';
export default {
name: 'app',
components: {
VueDragResize
},
data() {
return {
width: 0,
height: 0,
top: 0,
left: 0
}
},
methods: {
resize(newRect) {
this.width = newRect.width;
this.height = newRect.height;
this.top = newRect.top;
this.left = newRect.left;
}
}
}
</script>
What i want to do is to be able to click the "Propagate" button on the page, and have a <draggable></draggable> html element appended to the page, as follows:
<ul id="list">
<draggable></draggable>
</ul>
I'm completely stumped with this. Can anyone help me please?
v-if will do your job
Updated
You can use v-for as discussion:
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<button #click="propagate">Propagate</button>
<h3>Vue Modals</h3>
<ul id="list">
<draggable v-for="index in count" :key="index>
</draggable>
</ul>
<!-- <div v-html="template"></div> -->
</div>
</template>
<script>
import draggable from '#/components/Draggable.vue';
export default {
name: 'ModelPage',
components: {
draggable,
},
data () {
return {
msg: 'Welcome to the Modal Popup Page',
template: `<draggable></draggale>`,
count: 0
}
},
methods: {
propagate () {
// console.log("propagated")
this.count++
}
}
}
</script>