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>
Related
I have a Vue app that can either randomize a title and subtitle OR manually edit these two values through a custom input component. When a user decides to edit, their input should then display on save those results on the parent component.
I have the randomizer and child component emitting the updated headings working, but having a troubled time updating the parents and state to display the custom input title and subtitle on save and getting a "undefined" error for both title and subtitle when I placed console logs in updateTitleAndSubtitle() in the actions section of the store.
The objective of this code challenging is to return the new values to the store and be able to display the custom inputs while having the randomizer handy whenever a user decides to use that instead.
Any direction on what I'm doing wrong or missing would be much appreciated. I've been reading article after article around Vuex and Vue2 for 3 days now with 2 months of experience using Vue.
Custom Input Child Component:
<template>
<div>
<label for="title">Edit Title: </label>
<input
type="text"
id="title"
:updateTitle="updateTitle"
v-model="inputTitle"
/>
<label for="title">Edit Subtitle: </label>
<input
type="text"
id="subtitle" :updateSubtitle="updateSubtitle"
v-model="inputSubtitle"
/>
</div>
</template>
<script>
export default {
name: 'CustomInput',
props: {
title: String,
subtitle: String,
},
computed: {
updateTitle() {
console.log('updateTitle: ', this.title);
return this.title;
},
updateSubtitle() {
console.log('updateSubtitle: ', this.subtitle);
return this.subtitle;
},
inputTitle: {
get() {
console.log('set title: ', this.title);
return this.title;
},
set(title) {
console.log('set title: ', title);
this.$emit('input', title);
},
},
inputSubtitle: {
get() {
return this.subtitle;
},
set(subtitle) {
console.log('set subtitle: ', subtitle);
this.$emit('input', subtitle);
},
},
},
};
</script>
Parent component:
<template>
<main class="home-page page">
<div v-if="!editMode" class="display-information">
<div class="title">
<span class="bold">Title: </span>{{title}}
</div>
<div class="subtitle">
<span class="bold">Subtitle: </span>{{subtitle}}
</div>
<div class="controls">
<button id="randomize-button" class="control-button" #click="randomizeTitleAndSubtitle">
Randomize
</button>
<button id="edit-button" class="control-button" #click="onEdit">Edit</button>
</div>
</div>
<div v-else class="edit-controls">
<CustomInput
:title="title"
:subtitle="subtitle"
#update="v => onSave(v)"
/>
<div class="controls">
<button id="cancel-button" class="control-button" #click="onCancel">Cancel</button>
<button id="save-button" class="control-button" #click="onSave">Save</button>
</div>
</div>
</main>
</template>
<script>
// # is an alias to /src
import CustomInput from '#/components/CustomInput.vue';
import { mapState, mapActions } from 'vuex';
export default {
name: 'Home',
components: {
CustomInput,
},
data() {
return {
editMode: false,
};
},
computed: {
...mapState(['title', 'subtitle']),
},
methods: {
...mapActions(['randomizeHeadings', 'updateHeadings']),
onEdit() {
this.editMode = true;
},
onCancel() {
this.editMode = false;
},
onSave(v) {
this.editMode = false;
this.title = v.title;
this.subtitle = v.subtitle;
this.updateTitleAndSubtitle(v);
},
},
mounted() {
this.randomizeHeadings();
},
};
Vuex Store:
import randomWords from 'random-words';
export default new Vuex.Store({
state: {
title: '',
subtitle: '',
},
mutations: {
UPDATE_TITLE(state, value) {
state.title = value;
},
UPDATE_SUBTITLE(state, value) {
state.subtitle = value;
},
},
actions: {
randomizeTitle({ commit }) {
const newTitle = randomWords();
commit('UPDATE_TITLE', newTitle);
},
randomizeSubtitle({ commit }) {
const newSubtitle = randomWords();
commit('UPDATE_SUBTITLE', newSubtitle);
},
randomizeTitleAndSubtitle({ dispatch }) {
dispatch('randomizeTitle');
dispatch('randomizeSubtitle');
},
updateTitleAndSubtitle({ commit }) {
const payload = {
title: this.title || null,
subtitle: this.subtitle || null,
};
commit('UPDATE_TITLE', payload);
commit('UPDATE_SUBTITLE', payload);
},
},
modules: {
},
});
I tested your code in my local development environment and find out that you need a lot of changes in your codes to work better. Here is the new vuex store code:
vuex store:
export default new Vuex.Store({
state: {
title: '',
subtitle: '',
},
mutations: {
UPDATE_TITLE(state, value) {
state.title = value;
},
UPDATE_SUBTITLE(state, value) {
state.subtitle = value;
},
},
actions: {
randomizeTitle({ commit }) {
const newTitle = randomWords();
commit('UPDATE_TITLE', newTitle);
},
randomizeSubtitle({ commit }) {
const newSubtitle = randomWords();
commit('UPDATE_SUBTITLE', newSubtitle);
},
randomizeTitleAndSubtitle({ dispatch }) {
dispatch('randomizeTitle');
dispatch('randomizeSubtitle');
},
updateTitleAndSubtitle({ commit }, inputUser) {
/* I changed the structure of this action to work correctly */
console.log(inputUser);
commit('UPDATE_TITLE', inputUser.title);
commit('UPDATE_SUBTITLE', inputUser.subtitle);
},
},
modules: {
},
});
Also here is the new Parent component code:
Parent component:
<template>
<main class="home-page page">
<div v-if="!editMode" class="display-information">
<div class="title">
<span class="bold">Title: </span>{{title}}
</div>
<div class="subtitle">
<span class="bold">Subtitle: </span>{{subtitle}}
</div>
<div class="controls">
<button id="randomize-button" class="control-button" #click="randomizeTitleAndSubtitle">
Randomize
</button>
<button id="edit-button" class="control-button" #click="onEdit">Edit</button>
</div>
</div>
<div v-else class="edit-controls">
<CustomInput
:title="title"
:subtitle="subtitle"
#titleEvent = "myFuncTitle"
#subTitleEvent = "myFuncSubTitle"
/>
<!--
I removed this part from your component.
#update="v => onSave(v)"
and also added custom events (titleEvent and subTitleEvent) to the component
-->
<div class="controls">
<button id="cancel-button" class="control-button" #click="onCancel">Cancel</button>
<button id="save-button" class="control-button" #click="onSave">Save</button>
</div>
</div>
</main>
</template>
<script>
// # is an alias to /src
import CustomInput from '../components/CustomInput.vue';
import { mapActions } from 'vuex';
export default {
name: 'Parent',
components: {
CustomInput,
},
data() {
return {
editMode: false,
/* defining new data for handling "cancel" button functionality */
temporaryTitle: "",
temporarySubTitle: ""
};
},
computed: {
/* defining setter and getter for each computed value separately */
title: {
// getter
get: function () {
return this.$store.state.title;
},
// setter
set: function (newValue) {
this.$store.commit('UPDATE_TITLE', newValue);
}
},
subtitle: {
// getter
get: function () {
return this.$store.state.subtitle;
},
// setter
set: function (newValue) {
this.$store.commit('UPDATE_SUBTITLE', newValue);
}
},
},
methods: {
/* changing the name of actions according to the names defined in "store" */
...mapActions(['randomizeTitleAndSubtitle', 'updateTitleAndSubtitle']),
onEdit() {
this.editMode = true;
this.temporaryTitle = this.$store.state.title;
this.temporarySubTitle = this.$store.state.subtitle;
},
onCancel() {
this.editMode = false;
this.$store.commit('UPDATE_TITLE', this.temporaryTitle);
this.$store.commit('UPDATE_SUBTITLE', this.temporarySubTitle);
},
myFuncTitle(event) {
console.log(event);
/* we could not set values to "computed" properties, if we had not defined "set: function ..." for them above. */
this.title = event;
},
myFuncSubTitle(event) {
this.subtitle = event;
},
onSave(v) {
this.editMode = false;
console.log(v); /* "v" is not related to your data. notice the console */
// this.title = v.title;
// this.subtitle = v.subtitle;
const payload = {
title: this.title,
subtitle: this.subtitle,
};
this.updateTitleAndSubtitle(payload);
},
},
created() {
this.randomizeTitleAndSubtitle();
},
};
</script>
And finally here is the code of new Custom Input component:
Custom Input:
<template>
<div>
<label for="title">Edit Title: </label>
<input
type="text"
id="title"
v-model="inputTitle"
#input="$emit('titleEvent', $event.target.value)"
/>
<!-- emitting event like above code for each input -->
<label for="title">Edit Subtitle: </label>
<input
type="text"
id="subtitle"
v-model="inputSubtitle"
#input="$emit('subTitleEvent', $event.target.value)"
/>
</div>
</template>
<script>
export default {
name: 'CustomInput',
props: {
title: String,
subtitle: String,
},
computed: {
inputTitle: {
get() {
console.log('set title: ', this.title);
return this.title;
},
set(title) {
console.log('set title: ', title);
},
},
inputSubtitle: {
get() {
return this.subtitle;
},
set(subtitle) {
console.log('set subtitle: ', subtitle);
},
},
},
};
</script>
<style scoped>
</style>
I tried to comment some changes to the codes, but the main changes are related to changing the name of mapActions actions according to the names defined in "store" and also provide a setter for computed properties.
I suggest that you read more in vue and vuex documentations, especially the page that is related to custom events and computed setters and vuex actions, if you have problems with my codes.
I've searched and couldn't see any answer that fits what I need. I have a v-for loop with a button on each item and used VueClipboard2 to copy text. Anytime the button is clicked, I do some css changes to indicated the item that was copied. What happens is that, if there's more than 1 item, clicking on any button affects affect every other item and does the same effect.
I want to limit the clicking to the "own" item being clicked.
Here's my code:
<template>
<div class="form" id="shorten">
<form class="" #submit.prevent="shortener($event, value)">
<div>
<div class="form__shortener">
<input
class="form-input"
type="url"
name="link"
id="link"
placeholder="shorten a url here"
aria-label="input a url"
v-model="value"
/>
<button class="form-btn btn">
{{ buttonText }}
<p v-if="loading" class="loading"></p>
</button>
</div>
<SlideXLeftTransition :delay="100">
<p v-if="error" class="error">Please enter a valid link</p>
</SlideXLeftTransition>
</div>
</form>
<SlideYUpTransition group>
<div v-for="(link, index) in links" :key="index" class="form__links">
<p class="form__links-main">
{{ link.mainUrl }}
</p>
<div class="center form__links-copy">
<p>
<a :href="link.shortenedUrl" class="form__links-copy-link no-decoration">{{ link.shortenedUrl }}</a>
</p>
<button
class="form__links-copyBtn btn"
:class="[copied === true ? 'copied' : '']"
v-clipboard:copy="link.shortenedUrl"
v-clipboard:success="onCopy"
v-clipboard:error="onError"
>
<span v-if="!loading && !copied">Copy</span>
<span v-if="copied">Copied!</span>
</button>
</div>
</div>
</SlideYUpTransition>
</div>
</template>
<script>
import { required, minLength } from 'vuelidate/lib/validators';
import { SlideYUpTransition, SlideXLeftTransition } from 'vue2-transitions';
import axios from 'axios';
export default {
data() {
return {
value: '',
links: [],
message: '',
error: false,
loading: false,
buttonText: 'Shorten it!',
shortenedUrl: '',
copied: false,
};
},
validations: {
value: {
required,
minLength: minLength(1),
},
},
methods: {
async shortener(event, value) {
this.$v.$touch();
if (this.$v.$invalid) {
this.showError();
} else {
try {
this.loading = true;
this.buttonText = 'Loading';
const request = await axios.post('https://rel.ink/api/links/', { url: value });
this.loading = false;
this.buttonText = 'Shortened!';
setTimeout(() => {
this.buttonText = 'Shorten it!';
}, 1200);
this.shortenedUrl = `https://rel.ink/${request.data.hashid}`;
const mainUrl = request.data.url.length <= 20 ? request.data.url : `${request.data.url.slice(0, 30)}...`;
this.links.push({
shortenedUrl: `https://rel.ink/${request.data.hashid}`,
mainUrl,
});
localStorage.setItem('links', JSON.stringify(this.links));
} catch (error) {
this.showError();
console.log(error);
}
}
},
onCopy() {
this.copied = true;
setTimeout(() => {
this.copied = false;
}, 2500);
},
showError() {
this.error = true;
setTimeout(() => {
this.error = false;
}, 2000);
},
onError() {
alert('Sorry, there was an error copying that link. please reload!');
},
getLinks() {
if (localStorage.getItem('links')) this.links = JSON.parse(localStorage.getItem('links'));
},
},
components: {
SlideYUpTransition,
SlideXLeftTransition,
},
mounted() {
this.getLinks();
},
};
</script>
I would appreciate if anyone who help out.
Here's the live link: https://url-shortener-vue.netlify.app
To replicate, shorten two lines and click on the copy button on 1. It triggers all other items button.
Thank you.
Reason for your problem is
:class="[copied === true ? 'copied' : '']". SInce when you click any copy button, you change copied, and same class is used in all the iterations.
So, got the problem.
Solution is, you should have this copied corresponding to each link. So make your link as object.
link = [{ link: 'url...', copied: false}, {}, ...].
and, check for each link's copied value.
I've been practicing Vue.js recently.
Right now, I am making a simple todo list.
Some buttons does't work which filter tasks.
All button for listing every single tasks.
ongoing button is for uncheck input checkbox square.
done button is for checked ones.
Does anybody know how to fix?
new Vue({
el: '#app',
data: {
inputTask:'',
todos: [
{task:'task01', done: false},
{task:'task02', done: false},
{task:'task03', done: true},
{task:'task04', done: true},
{task:'task05', done: false},
]
},
computed: {
},
methods: {
allTask : function () {
this.todos
},
ongoingTask: function () {
this.todos = this.todos.filter(function (el) {
el.value = false
})
},
doneTask: function () {
this.todos = this.todos.filter(function (el) {
el.checked
})
},
addTask: function() {
const todo = { task: this.inputTask, done:false, };
this.todos.push(todo);
this.inputTask ='';
},
deleteTask: function (index) {
this.todos.splice(index, 1);
}
}
})
.done {text-decoration: line-through;}
<div id="app">
<input type="text" v-model="inputTask">
<button #click="addTask">Add</button>
<div>
<button #click="allTask">All</button>
<button #click="ongoingTask">Ongoing</button>
<button #click="doneTask">Done</button>
</div>
<ul>
<li v-for="(todo, index) in todos" :key="todo">
<input type="checkbox" v-model="todo.done">
<span :class="{done:todo.done}">{{todo.task}}</span>
<button #click="deleteTask(index)">Remove</button>
</li>
</ul>
<p v-show="todos.length===0">Nothing to do</p>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
You should use computed property to filter your task. And i guess you can add some improvements for user friendly interface. So your code will look like this:
new Vue({
el: '#app',
data: {
type: '',
inputTask:'',
todos: [
{task:'task01', done: false},
{task:'task02', done: false},
{task:'task03', done: true},
{task:'task04', done: true},
{task:'task05', done: false},
]
},
methods: {
setFilterType(type) {
this.type = type;
},
addTask() {
const todo = { task: this.inputTask, done: false};
this.todos.push(todo);
this.inputTask = '';
},
deleteTask(index) {
this.todos.splice(index, 1);
}
},
computed: {
filteredTodos() {
return this.todos.filter(todo => {
switch(this.type) {
case 'ongoing':
return !todo.done;
case 'done':
return todo.done;
default:
return true;
}
});
}
}
})
And template:
<div id="app">
<input type="text" v-model="inputTask">
<button #click="addTask">Add</button>
<div>
<button :class="{active: type ===''}" #click="setFilterType('')">All</button>
<button :class="{active: type ==='ongoing'}" #click="setFilterType('ongoing')">Ongoing</button>
<button :class="{active: type ==='done'}" #click="setFilterType('done')">Done</button>
</div>
<ul>
<li v-for="(todo, index) in filteredTodos" :key="todo">
<input type="checkbox" v-model="todo.done">
<span :class="{done:todo.done}">{{todo.task}}</span>
<button #click="deleteTask(index)">Remove</button>
</li>
</ul>
<p v-show="todos.length===0">Nothing to do</p>
</div>
Also i've added active class to button. So you can highlight via css current sorting.
More about computed you can read here
All your filter function on ongoingTask and doneTask are wrong, they should return a value; take a look at how filter function works: here.
I'm not sure what's the intended behavior of ongoingTaks is, but I guess the following should solve the problem. Please let me know and I'll update the answer correspondingly.
// ...
ongoingTask: function () {
this.todos = this.todos.filter(function (el) {
return el.value === false
})
},
doneTask: function () {
this.todos = this.todos.filter(function (el) {
return el.checked
})
},
// ...
EDIT:
The other part of the issue had to do with the logic, as soon as you clicked on any button that triggers the filtering, data in the state is removed (for instance, if I'm in the default view, viewing all todos, when I click in the Ongoing button, all the done TODO in the list gets removed and forever lost!); ideally you should keep that data and use computed properties to tackle the issue, here you have a working example (I'm using a Vue component here just for the sake of condensing all data in one place):
<template>
<div id="app">
<input type="text" v-model="inputTask">
<button #click="addTask">Add</button>
<div>
<button #click="changeFilter('all')">All</button>
<button #click="changeFilter('ongoing')">Ongoing</button>
<button #click="changeFilter('done')">Done</button>
</div>
<ul>
<li v-for="(todo, index) in getTasksList()" :key="{index}">
<input type="checkbox" v-model="todo.done">
<span :class="{done:todo.done}">{{todo.task}}</span>
<button #click="deleteTask(index)">Remove</button>
</li>
</ul>
<p v-show="todos.length===0">Nothing to do</p>
</div>
</template>
<script>
export default {
name: "App",
data: () => ({
inputTask: "",
currentFilter: "ongoing",
todos: [
{ task: "task01", done: false },
{ task: "task02", done: false },
{ task: "task03", done: true },
{ task: "task04", done: true },
{ task: "task05", done: false }
]
}),
computed: {
ongoingTasks: function() {
return this.todos.filter(function(todo) {
return todo.done === false;
});
},
doneTasks: function() {
return this.todos.filter(function(el) {
return el.done;
});
}
},
methods: {
changeFilter(newFilter) {
this.currentFilter = newFilter;
},
addTask: function() {
const todo = { task: this.inputTask, done: false };
this.todos.push(todo);
this.inputTask = "";
},
deleteTask: function(index) {
this.todos.splice(index, 1);
},
getTasksList() {
switch (this.currentFilter) {
case "ongoing":
return this.ongoingTasks;
case "done":
return this.doneTasks;
case "all":
default:
return this.todos;
}
}
}
};
</script>
I hope this helps!
Could you please tell me how to add an item to list on button click. Here is my code
https://plnkr.co/edit/hVQKk3Wl9DF3aNx0hs88?p=preview
var AddTODO = Vue.extend({
template: '#add-todo',
props: ['m'],
data: function () {
return {
message: ''
}
},
methods: {
addTodo: function () {
console.log(this.message)
console.log(this.m);
//this.m =this.message;
},
},
});
<template id="add-todo">
<div>
<input type="text" v-model="m">
<button #click="addTodo">Add todo</button>
</div>
</template>
is there any way to add items
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>