Bind event to multiple objects - javascript

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

Related

appending vuejs data to a div

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>

Vue v-if not hiding mobile header

I have this project where I try to add a mobile view menu. This menu is displayed by clicked a button. The button I have created switches a boolean on and off. But when the value of the boolean changes the v:if on the menu doesn't hide it. It keeps on showing.
This is my menu item inside the template:
<template>
<div>
<div> ... main menu ... </div>
<div :v-if="menuOpened" class="bg-purple-primary h-10 z-20">
<p>Hello World</p>
</div>
</div>
</template>
<script>
export default {
name: 'Header',
data () {
return {
menuOpened: false
}
},
methods: {
switchMenuState () {
this.menuOpened = !this.menuOpened
console.log(this.menuOpened)
}
}
}
</script>
You do not need that colon before v-if directive
<template>
<div>
<button #click="switchMenuState()">Switch</button>
<div v-if="menuOpened" class="bg-purple-primary h-10 z-20">
<p>Hello World</p>
</div>
</div>
</template>
<script>
export default {
name: "Header",
data() {
return {
menuOpened: false,
};
},
methods: {
switchMenuState() {
this.menuOpened = !this.menuOpened;
},
},
};
</script>

Localize Button click events on page using Vue

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>

Change modal window

I have 2 modal windows: register and login. When I click to "Sign Up" button, the modal window should change. What should I do?
This is a project link.
https://jsfiddle.net/Alienwave/0kqj7tr1/4/
Vue.component('signup', {
template: '#signup-template'
})
Vue.component('login', {
template: '#login-template',
data() {
return {
loginInput: '',
passwordInput: ''
}
},
methods: {
sendRequest(e) {
//code not here
},
changeModal() {
// THIS!!
}
}
});
new Vue({
el: "#app",
data() {
return {
showLogin: true,
showSignup: false
}
}
});
This is login template:
<template id="login-template">
<transition name="modal">
<div class="login-mask">
<div class="login-wrapper">
<div class="login-container">
<div class="login-footer">
<slot name="footer">
<div class="change-mode">
<button class="change-mode-reg" #click="">Sign up</button> <!-- THIS BUTTON SHOULD CHANGE MODAL! -->
</div>
</slot>
</div>
</div>
</div>
</div>
</transition>
</template>
Register template looks the same.
I cut a big chunk.
This is a good use case for Vue's custom events. I would update your code as follows:
#login-template
...
<div class="login-footer">
<slot name="footer">
<div class="change-mode">
<button class="change-mode-reg" #click="changeModal">Sign up</button>
<div class="change-mode-line"></div>
</div>
</slot>
</div>
...
login component
Vue.component('login', {
template: '#login-template',
data() {
return {
loginInput: '',
passwordInput: ''
}
},
methods: {
sendRequest(e) {
//code not here
},
changeModal() {
this.$emit('change');
}
}
});
#app
<div id="app">
<login v-if="showLogin" #close="showLogin = false" #change="changeModal"></login>
<signup v-if="showSignup" #close="showSignup = false"></signup>
</div>
Here is an updated fiddle.
(NOTE: it looks like you might have some other issues going on here, but this gets your modal switching issue fixed.)

How to Add or Remove Vue.js component dynamically (programmatically or on the fly)

Here is my code, this is just a example code, if the below works then this will help me to build something else that I am working on.
<template>
<div id="wrapper">
<div id="divOne">
<!-- Add or remove component here dynamically -->
</div>
<div id="divTwo">
<!-- Add or remove component here dynamically -->
</div>
<!-- There will be more divs like #divOne #divTwo above -->
<div>
<input type="radio" id="one" value="divOne" v-model="pickedDiv">
<label for="one">One</label>
</div>
<div>
<input type="radio" id="two" value="divTwo" v-model="pickedDiv">
<label for="two">Two</label>
</div>
<button #click="addComponent">Add Component</button>
</div>
</template>
<script>
import SomeComponent from './SomeComponent'
export default {
data() {
return {
pickedDiv: '',
pickedDivPreviously: ''
propItems: ['item1', 'item2']
}
}
methods: {
addComponent () {
//-- This is not working code but I need something like this --//
this.pickedDivPreviously = this.pickedDiv // event not sure how to get previously selected div
const divThatIsPicked = document.getElementById(this.pickedDiv)
const divThatWasPickedPreviously = document.getElementById(this.pickedDivPreviously)
// code here to remove/empty/destroy old component from 'divThatWasPickedPreviously'
divThatWasPickedPreviously.innerHTML = ""
// code here to add new component in 'divThatIsPicked'
divThatIsPicked.appendChild('<some-component :someProp="propItems" #someEvent="someFn">')
}
}
}
</script>
I don't want to distract you from answering actual question but If you are curious about what I am working then check this :) Here I am trying to add new child DIV at the end of the row when any row item is clicked.
I will be more than happy if this is converted to vue than the actual question asked above, as said please don't get distracted from actual question if you find it hard :)
I got help from JamesThomson in forum.vuejs.org, though the solution did not fix my issue but I got to understand the limitation or possibilities of using Vue.js.
JamesThomson says:
Yeah, your example code definitely won’t work. When working with Vue
you need to think in a data oriented way, not DOM oriented (like
jQuery)
Taken from your SO post:
Here I am trying to add new child DIV at the end of the row when any
row item is clicked.
I assume this is your end goal for this topic. A simple example of
this can be achieved like so:
https://codepen.io/getreworked/pen/XZOgbm?editors=1010
let Welcome = {
template: `
<p #click="toggleMsg()">Welcome {{ msg }}!</p>
`,
data () {
return {
msg: 'home'
}
},
methods: {
toggleMsg () {
return this.msg = this.msg === 'home' ? 'back' : 'home';
}
}
}
const App = new Vue({
el: '#app',
data: {
children: [
Welcome
]
},
methods: {
add () {
this.children.push(Welcome);
},
}
});
<link rel="stylesheet" href="//cdn.rawgit.com/milligram/milligram/master/dist/milligram.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
<template v-for="(child, index) in children">
<component :is="child" :key="child.name"></component>
</template>
<button #click="add()">Add Another</button>
</div>
or you can use a render function for more flexibility
https://jsfiddle.net/jamesbrndwgn/ku7m1dp0/9/
const Reusable = {
template: '<div>{{ name }} {{ bar }}</div>',
props: {
name: {
type: String
}
},
data () {
return {
bar: 'Bar'
}
}
}
const App = new Vue({
el: '#app',
data: {
items: []
},
methods: {
addComponent () {
const renderComponent = {
render (h) {
return h(Reusable, {
class: ['foo'],
props: {
name: 'Foo'
}
})
}
}
this.items.push(renderComponent)
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<link rel="stylesheet" href="//cdn.rawgit.com/milligram/milligram/master/dist/milligram.min.css">
<div id="app">
<component v-for="item in items" ref="itemRefs" :is="item" :key="item.name"></component>
<button #click="addComponent">Add Component</button>
</div>
I found one of the other ways that does kinda same as above but work only with old vue.js-1 not with vue.js-2:
var createNewBox = function() {
var MyPartial = Vue.extend({});
window.partial = new MyPartial({
template: '#partial',
data: function() {
return {
txt: 'This is partial'
}
},
methods: {
print: function() {
console.log('this.txt : ' + this.txt)
console.log('main.txt : ' + main.txt)
},
},
})
window.partial.$mount().$appendTo('body')
}
window.main = new Vue({
el: '#main',
data: function() {
return {
txt: 'This is main'
}
},
methods: {
show: function() {
createNewBox()
}
},
})
<script src="https://cdn.bootcss.com/vue/1.0.17/vue.min.js"></script>
<div #click="show" style="width:200px;height:200px;background:#000" id="main">
<template id="partial">
<div style="width:100px;height:100px;background:#ff0" #click.stop="print"></div>
</template>
</div>
this converted to Vue.
https://codepen.io/jacobgoh101/pen/Kojpve
<div id="app">
<div class="parent">
<div class="child" #click="handleChildClick" data-new-child-id="1">1234</div>
<div class="child" #click="handleChildClick" data-new-child-id="2">12341234 </div>
<div class="child" #click="handleChildClick" data-new-child-id="3">123412341234</div>
<div class="child" #click="handleChildClick" data-new-child-id="4">1234</div>
<div class="new-child" v-if="[1,2,3,4].indexOf(showNewChild) > -1">boom</div>
<div class="child" #click="handleChildClick" data-new-child-id="5">12341234</div>
<div class="child" #click="handleChildClick" data-new-child-id="6">123412341234</div>
<div class="child" #click="handleChildClick" data-new-child-id="7">1234</div>
<div class="child" #click="handleChildClick" data-new-child-id="8">12341234</div>
<div class="new-child" v-if="[5,6,7,8].indexOf(showNewChild) > -1">boom</div>
<div class="child" #click="handleChildClick" data-new-child-id="9">123412341234</div>
<div class="new-child" v-if="[9].indexOf(showNewChild) > -1">boom</div>
</div>
</div>
Javascript
new Vue({
el: '#app',
data: {
showNewChild:null
},
methods: {
handleChildClick(e) {
let id = e.target.dataset.newChildId;
id = Number(id);
this.showNewChild = id;
}
}
})

Categories