I have multiple buttons a page and when I click one to toggle the buttons text, ALL of the buttons change. How do I use VUE to separate functionality and individualize each buttons #click to only affect the clicked button? In the snippet below you will see that all buttons change on click. I only want the clicked buttons text to change.
See example below. This example is from another post and all credit goes to:
change button while pressing it with vue.js
new Vue({
el: '#app',
data: {
isFavorite: false
},
methods: {
toggleFavorite() {
this.isFavorite = !this.isFavorite;
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<button #click="toggleFavorite" v-show="!isFavorite">Add to favorites</button>
<button #click="toggleFavorite" v-show="isFavorite">Delete from favorites</button>
<button #click="toggleFavorite" v-show="!isFavorite">Add to favorites</button>
<button #click="toggleFavorite" v-show="isFavorite">Delete from favorites</button>
</div>
Use a dictionary (object) instead of a single variable:
data() {
return {
isFavorite: {}
}
},
Instead of toggling a single boolean, toggle a property on the object:
<button #click="toggleFavorite('item1')" v-show="!isFavorite['item1']">Add to favorites</button>
<button #click="toggleFavorite('item1')" v-show="isFavorite['item1']">Delete from favorites</button>
The toggle:
methods: {
toggleFavorite(item) {
this.$set(this.isFavorite, item, !this.isFavorite[item]);
}
}
Demo if this is still unclear:
new Vue({
el: '#app',
data() {
return {
isFavorite: {}
}
},
methods: {
toggleFavorite(item) {
this.$set(this.isFavorite, item, !this.isFavorite[item]);
}
}
})
<div id="app">
<button #click="toggleFavorite('item100')" v-show="!isFavorite['item100']">Add to favorites</button>
<button #click="toggleFavorite('item100')" v-show="isFavorite['item100']">Delete from favorites</button>
<button #click="toggleFavorite('item101')" v-show="!isFavorite['item101']">Add to favorites</button>
<button #click="toggleFavorite('item101')" v-show="isFavorite['item101']">Delete from favorites</button>
<hr>
<div v-for="x in 10" :key="x">
<button #click="toggleFavorite(`item${x}`)" v-show="!isFavorite[`item${x}`]">Add to favorites</button>
<button #click="toggleFavorite(`item${x}`)" v-show="isFavorite[`item${x}`]">Delete from favorites</button>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
It happens because of the v-show values are same. I think this implementation is the not best practise for Vue. You can use v-if v-else for data value or just change the text by value.
new Vue({
el: '#app',
data: {
isFavorite: false,
isFavorite2:false
},
methods: {
toggleFavorite() {
this.isFavorite = !this.isFavorite;
},
toggleFavorite2() {
this.isFavorite2 = !this.isFavorite2;
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<button #click="toggleFavorite" v-show="!isFavorite">Add to favorites</button>
<button #click="toggleFavorite" v-show="isFavorite">Delete from favorites</button>
<button #click="toggleFavorite2" v-show="!isFavorite2">Add to favorites 2</button>
<button #click="toggleFavorite2" v-show="isFavorite2">Delete from favorites 2</button>
</div>
Related
I have something I have been trying to do which is challenging. I have a simple div that gets appended to the top level on the click of a button. The issue is I want vuejs data appended inside of this div. I am not quite sure how to bind this data, I have tried using a simple expression, but no luck. Can someone please let me know how this can be solved--and with a div, not a bootstrap modal
new Vue({
el: "#app",
data: {
chocs:[{"title":'a man tale'},{"title":'a boy tale'}]
},
methods: {
handleTileClick: function(){
alert(this.chocs[0].title);
$("#fox").append(`<div id="popover-dialog"> data here {{this.chocs[0].title}}
</div>`);
},
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<div id="app">
<h2>Todos:</h2>
<button v-on:click="handleTileClick()">
Click Me
</button>
<div id="fox">
</div>
</div>
Without JQuery, the Vue-way:
new Vue({
el: "#app",
data: () => ({
chocs:[{"title":'a man tale'}, {"title":'a boy tale'}],
titleToBeAppended: ''
}),
methods: {
handleTileClick: function(){
this.titleToBeAppended = this.chocs[0].title
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div id="popover-dialog">
<p>Data here: {{ titleToBeAppended }}</p>
<button v-on:click="handleTileClick()">Click Me</button>
</div>
</div>
Using template literals:
new Vue({
el: "#app",
data: () => ({
chocs:[{"title":'a man tale'}, {"title":'a boy tale'}]
}),
methods: {
handleTileClick: function(){
alert(this.chocs[0].title);
$("#fox").append(
`<div id="popover-dialog">data here: ${this.chocs[0].title}</div>`
);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<div id="app">
<h2>Todos:</h2>
<button v-on:click="handleTileClick()">Click Me</button>
<div id="fox"></div>
</div>
I'm trying to attach the same behavior to several different elements and haven't been able to figure out how.
Given the below snippet of code, if you were to click on the button the events fire for both buttons simultaneously. I'm wondering if it's possible to trigger the event for only the button that was clicked without resorting to creating methods for button1 and showInside1, etc.
var App = new Vue({
el: '#app',
data() {
return {
showInside: false
}
},
methods:{
show: function () {
this.showInside = true
},
hide: function () {
console.log('hide')
this.showInside = false
}
},
events: {
closeEvent: function () {
console.log('close event called')
this.hide()
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click.stop="show">1 Click me</button>
<div v-if="showInside">
<p>This is box 1</p>
<button #click="hide">Close</button>
</div>
<button #click.stop="show">2 Click me</button>
<div v-if="showInside">
<p>This is box 2</p>
<button #click="hide">Close</button>
</div>
</div>
Consider moving the common code to a component with slots to reduce the repetitive code and to isolate the data properties:
Vue.component('my-section', {
template: `
<div class="my-section">
<button #click.stop="showInside = true">Click me</button>
<div v-if="showInside">
<slot></slot>
<button #click="showInside = false">Close</button>
</div>
</div>`,
data() {
return {
showInside: false
}
},
methods:{
show: function () {
this.showInside = true
},
hide: function () {
console.log('hide')
this.showInside = false
}
},
events: {
closeEvent: function () {
console.log('close event called')
this.hide()
}
}
})
var App = new Vue({
el: '#app',
})
.my-section {
margin: 1em;
padding: 0.5em;
border: solid 1px #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<my-section>
<p>This is box 1</p>
</my-section>
<my-section>
<p>This is box 2</p>
</my-section>
</div>
While showInside is Boolean it's impossible. You should keep each box's state:
data() {
return {
show: {
panel1: false,
panel2: false,
}
}
},
then:
<div id="app">
<button #click.stop="show.panel1 = true">1 Click me</button>
<div v-if="show.panel1">
<p>This is box 1</p>
<button #click="show.panel1 = false">Close</button>
</div>
<button #click.stop="show.panel2 = true">2 Click me</button>
<div v-if="show.panel2">
<p>This is box 2</p>
<button #click="show.panel2 = false">Close</button>
</div>
</div>
Of course you can use arrays and v-for for dynamic cases
am having problem switching from the first section(v-if) to the second second(v-else) section using v-if. as default now section(v-if) is displaying and i created a button(startGame) to change to the second section(v-else) when clicked but i find it not easy to do please help me out
<section class="row controls" v-if="!gameIsrunning">
<div class="small-12 columns">
<button id="start-game" #click="startGame">START NEW GAME</button>
</div>
</section>
<section class="row controls" v-else>
<div class="small-12 columns">
<button id="attack">ATTACK</button>
<button id="special-attack">SPECIAL ATTACK</button>
<button id="heal">HEAL</button>
<button id="give-up">GIVE UP</button>
</div>
</section>
<script>
let app = new Vue({
el: "#app",
data: {
playerHealth: 100,
monsterHealth: 100,
gameIsRunning: false
},
methods: {
startGame: function() {
this.gameIsRunning = true;
}
}
});
</script>
I'm making cart app with Vue. And trying to make quantity counter, but when I click - or + button, all of items quantity also increase or decrease.
So it seems like I need to give each key for buttons but I don't know how to do that.
new Vue({
el: '#app',
data(){
return {
foods: [{
id: 1,
imgUrl: 'https://image.shutterstock.com/image-photo/healthy-food-clean-eating-selection-260nw-761313058.jpg',
title: 'Food',
price: '5,00'
}],
num:0
}
},
methods:{
increase(index){
this.num++
},
decrease(index){
this.num --
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div
class="items" v-for="(food,index) in foods"
v-bind:food="food"
v-bind:key="food.id"
>
<img class="foodImg" v-bind:src="food.imgUrl" />
<h4>{{food.title}}</h4>
<p>{{food.price}}€</p>
<button :class="index" class="minus" #click="decrease">-</button>
{{num}}
<button :class="index" class="add" #click="increase">+</button>
<button type="submit">Add to cart</button>
</div>
</div>
Your num variable shouldn't be in your component and instead you should attach it to your food items. Otherwise the num variable is shared across all of them.
Please don't forget to give your food items the num variable before you pass the foods array to your component so its not initially empty.
try this:
<div class="items" v-for="(food,index) in foods" v-bind:food="food" v-bind:key="food.id">
<img class="foodImg" v-bind:src="food.imgUrl"/>
<h4>{{food.title}}</h4>
<p>{{food.price}}€</p>
<button :class="index" class="minus" #click="increase(food)">-</button>
{{food.num}}
<button :class="index" class="add" #click="decrease(food)">+</button>
<button type="submit">Add to cart</button>
</div>
Script
<script>
export default {
name:'Products',
props:['foods'],
methods:{
increase(food){
food.num++
},
decrease(index){
food.num--
}
}
}
I have a project here where I need to add and remove layers of images, using vue.js 2. I am building up a pizza where I need to add toppings. My current solution has a flaw - it removes all other elements/ pizza toppings when I add a new one.
The toppings are generated from an array which I loop through.
Can you please help, I am sure this is easy but me being a rookie in vue.js I have already struggled for hours... Thanks!
<div id="app" class="container-fluid">
<div class="row">
<div class="left-container">
<h2>add your ingredients:</h2>
<div v-for="(item, index) in pizzas" v-bind:key="index">
<button class="btn btn-primary" v-on:click="show == index ? show = -1 : show = index">{{ item.pizza }}</button>
</div>
<div class="submit-buttons">
<button class="btn btn-primary reset-pizza" v-on:click="show = -1">Reset pizza</button>
<button class="btn btn-primary submit-pizza">Share pizza</button>
</div>
</div>
<div class="right-container">
<ul class="pizza-layers">
<li v-for="(item, index) in pizzas" class="pizza-canvas" v-bind:class="item.class" v-if="show == index"></li>
<li class="pizza-canvas pizza-canvas--topping-base"></li>
</ul>
</div>
</div>
</div>
<script>
new Vue({
el: '#app',
data: {
pizzas: [
{ pizza: 'Salami', class: 'pizza-canvas--topping-salami' },
{ pizza: 'Rucolla', class: 'pizza-canvas--topping-rucolla' },
{ pizza: 'Cheese', class: 'pizza-canvas--topping-cheese' }
],
show: {},
},
})
</script>
To allow for multiple toppings, this.show should be an array, instead of an object.
Once you change that, you'd need to modify the click event handler to add/remove the topping based on whether or not the topping is already part of show.
Also, while displaying the topping, you'd need to check its existence using show.includes(index) instead of show == index - since this.show is an array.
In the snippet below, I've applied these changes, and added some background colors to visualize how the toppings are added or removed.
new Vue({
el: '#app',
data: {
pizzas: [{
pizza: 'Salami',
class: 'pizza-canvas--topping-salami'
},
{
pizza: 'Rucolla',
class: 'pizza-canvas--topping-rucolla'
},
{
pizza: 'Cheese',
class: 'pizza-canvas--topping-cheese'
}
],
show: [],
},
methods: {
addTopping(event, item, index) {
if(this.show.includes(index)) {
this.show.splice(this.show.indexOf(index),1);
} else {
this.show.push(index);
}
}
}
})
.pizza-canvas--topping-salami {
background-color: red;
}
.pizza-canvas--topping-rucolla {
background-color: yellow;
}
.pizza-canvas--topping-cheese {
background-color: blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js"></script>
<div id="app" class="container-fluid">
<div class="row">
<div class="left-container">
<h2>add your ingredients:</h2>
<div v-for="(item, index) in pizzas" v-bind:key="index">
<button class="btn btn-primary" v-on:click="addTopping(event, item, index)">{{ item.pizza }}</button>
</div>
<div class="submit-buttons">
<button class="btn btn-primary reset-pizza" v-on:click="show = []">Reset pizza</button>
<button class="btn btn-primary submit-pizza">Share pizza</button>
</div>
</div>
<div class="right-container">
<ul class="pizza-layers">
<li v-for="(item, index) in pizzas" class="pizza-canvas" v-bind:class="item.class" v-if="show.includes(index)"></li>
<li class="pizza-canvas pizza-canvas--topping-base"></li>
</ul>
</div>
</div>
</div>