click and mouse event conflict in vuejs - javascript

recently,i try to write a todo demo via vuejs,however,when i write the following code
<div id="item_lists">
<div class='user_choice_item' v-for="todo in todos" #mouseenter="showDeleteBtn($index,$event)" v-on:mouseleave.self="hideDeleteBtn($index,$event)">
<input type='checkbox' name='item_cbx' v-model="todo.checked" />
<label class='with_cbx_item'>{{todo.content}}</label>
<span class='delete_bt' v-on:click.stop="deleteTodo(todo)" v-show="todo.show"></span>
</div>
</div>
//js
var todoList = new Vue({
el:"#item_lists",
data:{
todos:[]
},
methods:{
showDeleteBtn:function(index,event){
event.stopPropagation();
if(event.currentTarget.className!=="user_choice_item")
return;
var newState = Object.assign({},this.todos[index],{show:true});
this.todos.$set(index,newState);
},
hideDeleteBtn:function(index,event){
var newState = Object.assign({},this.todos[index],{show:false});
this.todos.$set(index,newState);
},
deleteTodo:function(todo){
this.todos.$remove(todo);
return false;
},
}
});
it turns out that the only the mouseenter event can be triggered correctly, the click on "delete_btn" and change event on the checkbox ,the mouseleave event not triggered. however, when i remove the mouseenter event of the parent div. the child events work .i wanna know what causes this ..can anyone help me?

I think you may be working too hard at handling the event. Here's a snippet that does what I think you want it to do. The showDeleteBtn and hideDeleteBtn methods simply set the show data member of the todo item.
var todoList = new Vue({
el: "#item_lists",
data: {
todos: [{
content: 'Hi there',
checked: true,
show: false
},
{
content: 'Another',
checked: true,
show: false
}]
},
methods: {
showDeleteBtn: function(index) {
this.todos[index].show = true;
},
hideDeleteBtn: function(index) {
this.todos[index].show = false;
},
deleteTodo: function(todo) {
this.todos.$remove(todo);
return false;
}
}
});
.delete_bt {
background-color: lightblue;
padding: 0.3em 1em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<div id="item_lists">
<div class='user_choice_item' v-for="todo in todos" v-on:mouseenter="showDeleteBtn($index)" v-on:mouseleave="hideDeleteBtn($index)">
<input type='checkbox' name='item_cbx' v-model="todo.checked" />
<label class='with_cbx_item'>{{todo.content}}</label>
<span class='delete_bt' v-on:click.stop="deleteTodo(todo)" v-show="todo.show">Delete</span>
</div>
</div>

Related

how to bind value of v-for to v-if

I'm working with BootstrapVue. To my problem: I have a v-for in my template in which I have two buttons.
Looping over my v-for my v-if doesn't generate unique IDs and than after clicking one button each button will be triggered (from Open me! to Close me! and other way around).
How can I manage to get each button only triggers itself and doesn't affect the other?
I think I have to use my n of my v-for but I actually don't know how to bind this to a v-if..
Thanks in advance!
<template>
<div>
<div v-for="n in inputs" :key="n.id">
<b-button v-if="hide" #click="open()">Open me!</b-button>
<b-button v-if="!hide" #click="close()">Close me! </b-button>
</div>
<div>
<b-button #click="addInput">Add Input</b-button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
id: null,
inputs: [{
id: 0
}],
hide: true,
};
},
methods: {
open() {
this.hide = false
},
close() {
this.hide = true
},
addInput() {
this.inputs.push({
id: this.id += 1;
})
}
}
};
</script>
Everything seems to look fine. In order to handle each button triggers,
you can maintain an object like so:
<script>
export default {
data() {
return {
inputs: [{id: 0, visible: false}],
};
},
methods: {
open(index) {
this.inputs[index].visible = false
},
close(index) {
this.inputs[index].visible = true
},
addInput() {
this.inputs.push({id: this.inputs.length, visible: false});
}
}
};
</script>
and your template should be like
<template>
<div>
<div v-for="(val, index) in inputs" :key="val.id">
<b-button v-if="val.visible" #click="open(index)">Open me!</b-button>
<b-button v-if="!val.visible" #click="close(index)">Close me! </b-button>
</div>
</div>
</template>
Edit:
You don't need to insert an id every time you create a row, instead can use the key as id. Note that the inputs is an object and not array so that even if you want to delete a row, you can just pass the index and get it removed.
I would create an array of objects. Use a boolean as property to show or hide the clicked item.
var app = new Vue({
el: '#app',
data: {
buttons: []
},
created () {
this.createButtons()
this.addPropertyToButtons()
},
methods: {
createButtons() {
// Let's just create buttons with an id
for (var i = 0; i < 10; i++) {
this.buttons.push({id: i})
}
},
addPropertyToButtons() {
// This method add a new property to buttons AFTER its generated
this.buttons.forEach(button => button.show = true)
},
toggleButton(button) {
if (button.show) {
button.show = false
} else {
button.show = true
}
// We are changing the object after it's been loaded, so we need to update ourselves
app.$forceUpdate();
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<template>
<div>
<div v-for="button in buttons" :key="button.id">
<button v-if="button.show" #click="toggleButton(button)">Open me!</button>
<button v-if="!button.show" #click="toggleButton(button)">Close me! </button>
</div>
</div>
</template>
</div>

Hiding element with v-show and rendering it back with a keydown event in Vue

I have a div containing icons that I am hiding with a boolean value and v-show directive:
<div class="room" v-show=show.room #click='toggle = !toggle'>
<div class="col-lg-6 draw-icon-container" v-show="toggle">
<p>Show</p>
</div>
</div>
data: () => ({
toggle: true,
}),
The desired div gets hid by clicking on it, but since it is removed I have no way to display it back, how can I render it again after the user has pressed the enter key? Something like this:
if (e.keyCode == 13) {
this.toggle = false
}
var app = new Vue({
el: '#app',
data: {
show: {room: true},
toggle: true
},
created: function () {
// listen for key event
window.addEventListener('keyup', this.toggleMethod)
},
methods: {
toggleMethod() {
this.toggle = true;
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app" class="room" v-show=show.room #click='toggle = !toggle' style="background:red;width:100%;height:40px;">
<div class="col-lg-6 draw-icon-container" v-show="toggle">
<p>Show (Click me to toggle show/hide)</p>
</div>
</div>
To access the scoped variable of the vue component that defined in data(), you must use this keyword, hence to change the value of toggle, you must use this.toggle = false

Click-and-edit text input with Vue

I'm looking for a click-and-edit Vue component.
I've found a fiddle and made some edits. It works like this:
The fiddle is here.
The problem: I need an additional click to make the input focused. How can I make it focused automatically?
The code from the fiddle. HTML:
<div id="app">
Click the values to edit!
<ul class="todo-list">
<li v-for = "todo in todos">
<input v-if = "todo.edit" v-model = "todo.title"
#blur= "todo.edit = false; $emit('update')"
#keyup.enter = "todo.edit=false; $emit('update')">
<div v-else>
<label #click = "todo.edit = true;"> {{todo.title}} </label>
</div>
</li>
</ul>
</div>
JS:
new Vue({
el: '#app',
data: {
todos: [{'title':'one value','edit':false},
{'title':'one value','edit':false},
{'title':'otro titulo','edit':false}],
editedTodo: null,
message: 'Hello Vue.js!'
},
methods: {
editTodo: function(todo) {
this.editedTodo = todo;
},
}
})
You can use a directive, for example
JS
new Vue({
el: '#app',
data: {
todos: [
{ title: 'one value', edit: false },
{ title: 'one value', edit: false },
{ title: 'otro titulo', edit: false }
],
editedTodo: null,
message: 'Hello Vue.js!'
},
methods: {
editTodo: function (todo) {
this.editedTodo = todo
}
},
directives: {
focus: {
inserted (el) {
el.focus()
}
}
}
})
HTML
<div id="app">
Click the values to edit!
<ul class="todo-list">
<li v-for="todo in todos">
<input
v-if="todo.edit"
v-model="todo.title"
#blur="todo.edit = false; $emit('update')"
#keyup.enter="todo.edit=false; $emit('update')"
v-focus
>
<div v-else>
<label #click="todo.edit = true;"> {{todo.title}} </label>
</div>
</li>
</ul>
</div>
You can find more info here
https://v2.vuejs.org/v2/guide/custom-directive.html
With #AitorDB's help I have written a Vue component for this, I call it Click-to-Edit. It is ready to use, so I'm posting it.
What it does:
Supports v-model
Saves changes on clicking elsewhere and on pressing Enter
ClickToEdit.vue: (vue 2.x)
<template>
<div>
<input type="text"
v-if="edit"
:value="valueLocal"
#blur.native="valueLocal = $event.target.value; edit = false; $emit('input', valueLocal);"
#keyup.enter.native="valueLocal = $event.target.value; edit = false; $emit('input', valueLocal);"
v-focus=""
/>
<p v-else="" #click="edit = true;">
{{valueLocal}}
</p>
</div>
</template>
<script>
export default {
props: ['value'],
data () {
return {
edit: false,
valueLocal: this.value
}
},
watch: {
value: function() {
this.valueLocal = this.value;
}
},
directives: {
focus: {
inserted (el) {
el.focus()
}
}
}
}
</script>
Edit for 3.x: [Breaking changes between 2.x and 3.x]
remove .native from the event handlers
change the focus hook to mounted as described in Custom Directives 3.x.
Built on #Masen Furer's work. I added some protection to handle when a user deletes all of the data. There is probably a way to accomplish this using "update" but I couldn't get it working.
I also added the ability to hit escape and abandon any changes.
<template>
<span>
<input type="text"
v-if="edit"
:value="valueLocal"
#blur="save($event);"
#keyup.enter="save($event);"
#keyup.esc="esc($event);"
v-focus=""/>
<span v-else #click="edit = true;">
{{valueLocal}}
</span>
</span>
</template>
<script>
export default {
props: ['value'],
data () {
return {
edit: false,
valueLocal: this.value,
oldValue: (' ' + this.value).slice(1)
}
},
methods: {
save(event){
if(event.target.value){
this.valueLocal = event.target.value;
this.edit = false;
this.$emit('input', this.valueLocal);
}
},
esc(event){
this.valueLocal = this.oldValue;
event.target.value = this.oldValue;
this.edit = false;
this.$emit('input', this.valueLocal);
}
},
watch: {
value: function() {
this.valueLocal = this.value;
}
},
directives: {
focus: {
inserted (el) {
el.focus()
}
}
}
}
</script>

how to add or remove class in vuejs 2 based on id

I want to add or remove class to an element based on id using vuejs, but this code applies the behaviour to all elements. How can I add/remove a class to the element by id? Thanks all in advance.
new Vue({
el:"#test",
data: {
msg: 'msg goes here',
isActive: false
},
methods: {
log: function(e) {
this.isActive = ! this.isActive
//alert(e.currentTarget.id);
}
}
});
.active {
background: red
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.13/dist/vue.js"></script>
<div id="test">
<div id="1" v-on:click="log($event)" v-bind:class="{ active: isActive }">
{{ msg }}
</div>
<div id="2" v-on:click="log($event)" v-bind:class="{ active: isActive }">
{{ msg }}
</div>
</div>
Here is an example of using components to achieve the behavior you are looking for:
// register
const clickable = Vue.component('clickable', {
props: ['msg'],
data: function() {
return {
isActive: false
}
},
template: '<div :class="{ active: isActive }" #click="isActive = !isActive">{{ msg }}</div>'
})
new Vue({
el: "#test",
components: {
clickable
},
data: function() {
return {
msg: 'msg goes here',
isActive: false
}
}
});
.active {
background: red
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.13/dist/vue.js"></script>
<div id="test">
<clickable :msg='msg'></clickable>
<clickable :msg='msg'></clickable>
</div>
You have not internalized basic vuejs concepts.
https://v2.vuejs.org/v2/guide/components.html
Both of your elements should be separate components.

V-bind with dynamically rendered components

I have a simple code where I have got input fields for the user and the user can click on a button to render more input fields, and I want to store those input fields.
<div id='wordslist'>
<div class='announcement'>Voer hieronder uw woorden in</div>
<div class='wordsdiv'>
<div id='onedictspec' v-for='arrayspot in wordupload.wordcount'>
<input type='text' class='inputwordtext' v-model='arrayspot[wordupload.wordcount][0]'>
<input type='text' class='inputwordtext' v-model='arrayspot[wordupload.wordcount][1]'>
</div>
</div>
<div id='morewords'>
<button class='morewordsbtn active hover' v-on:click='morewords'>More words</button>
</div>
Vuejs
data{
wordupload: {
wordcount: 20,
wordinput: [],
}
}
methods: {
morewords: function () {
this.wordupload.wordcount += 30
}
}
In short it renderes wordupload.wordcount number of #wordsdiv, and I am tring to give the input of those wordsdiv a spot in the wordinput array. But I can't v-model their value to the spot if it doesn't exist. Any ideas on what would be the best way to store those dynamically rendered input values would be much appreciated.
Are you looking for something like this?
https://jsfiddle.net/2nn58fa8/1/
Vue.component('wordlist', {
data: function() {
return {
wordupload: {
wordcount: 20,
wordinput: [],
}
}
},
created: function() {
this.wordupload.wordinput.length = this.wordupload.wordcount;
},
methods: {
morewords: function() {
this.wordupload.wordcount += 30;
this.wordupload.wordinput.length = this.wordupload.wordcount;
},
checkwords: function() {
console.log(this.wordupload.wordinput);
}
}
});
var vue = new Vue({
el: '#app'
});

Categories