1 为todoList案例新增编辑功能
修改UserItem.vue
<template>
<li>
<label>
<input type="checkbox"
:checked="item.done"
@click="handleClick(item.id)"/>
<span v-show="!item.isEdit">{{ item.title }}</span>
<input type="text" v-show="item.isEdit" :value="item.title" @blur="handleBlur(item, $event)">
</label>
<button class="btn btn-danger" @click="handleDelete(item.id)">删除</button>
<button class="btn btn-edit" @click="handleEdit(item)">编辑</button>
</li>
</template>
<script>
export default {
name: `UserItem`,
props: ['item'],
methods: {
handleClick(id) {
this.$bus.$emit('checkTodo', id)
},
handleDelete(id) {
if (confirm('确定删除吗?')) {
this.$bus.$emit('deleteTodo', id)
}
},
handleBlur(item, event) {
item.isEdit = false
if (!event.target.value.trim()) {
return alert('输入不能为空 !!!')
} else {
this.$bus.$emit('updateTodoById', item.id, event.target.value.trim())
}
},
handleEdit(item) {
if (item.hasOwnProperty('isEdit')) {
item.isEdit = true
} else {
this.$set(item, 'isEdit', true)
}
},
}
}
</script>
<style scoped>
/* item */
li {
list-style: none;
height: 36px;
line-height: 36px;
padding: 0 5px;
border-bottom: 1px solid #ddd;
}
li label {
float: left;
cursor: pointer;
}
li label li input {
vertical-align: middle;
margin-right: 6px;
position: relative;
top: -1px;
}
li button {
float: right;
display: none;
margin-top: 3px;
}
li:before {
content: initial;
}
li:last-child {
border-bottom: none;
}
li:hover {
background-color: #ddd;
}
li:hover button {
display: block;
}
</style>
修改App.vue:
<template>
<div class="todo-container">
<div class="todo-wrap">
<UserHeader :addObjToTodoList="addObjToTodoList"/>
<UserList :todoList="todoList"/>
<UserFooter :todoList="todoList"
:clearAllTodo="clearAllTodo"
:checkAllTodo="checkAllTodo"/>
</div>
</div>
</template>
<script>
import UserHeader from "@/components/UserHeader"
import UserFooter from "@/components/UserFooter"
import UserList from "@/components/UserList"
export default {
name: 'App',
data() {
return {
todoList: JSON.parse(localStorage.getItem('todoList')) || []
}
},
methods: {
addObjToTodoList(obj) {
this.todoList.unshift(obj)
},
checkTodo(id) {
this.todoList
.filter(item => item.id === id)
.map(item => item.done = !item.done);
},
deleteTodo(id) {
this.todoList = this.todoList
.filter(item => item.id !== id)
},
checkAllTodo(checked) {
console.log(checked)
if (checked) {
this.todoList.map(item => {
item.done = checked
})
} else {
this.todoList.map(item => {
item.done = false
})
}
},
clearAllTodo() {
if (confirm('确认清空列表吗?')) {
this.todoList.map(item => {
if (item.done === true) {
item.done = false
}
})
}
},
updateTodoById(id, value) {
this.todoList
.filter(item => item.id === id)
.map(item => item.title = value)
}
},
watch: {
todoList: {
// 深度监视
deep: true,
handler(newValue, oldValue) {
localStorage.setItem('todoList', JSON.stringify(newValue))
}
},
},
mounted() {
this.$bus.$on('checkTodo', this.checkTodo)
this.$bus.$on('deleteTodo', this.deleteTodo)
this.$bus.$on('updateTodoById', this.updateTodoById)
},
beforeDestroy() {
this.$bus.$off('checkTodo')
this.$bus.$off('deleteTodo')
this.$bus.$off('updateTodoById')
},
components: {UserHeader, UserList, UserFooter}
}
</script>
<style>
/* base */
body {
background-color: #fff;
}
.btn {
display: inline-block;
padding: 4px 12px;
margin-bottom: 0;
font-size: 14px;
line-height: 20px;
text-align: center;
vertical-align: middle;
cursor: pointer;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
border-radius: 4px;
}
.btn-danger {
color: #fff;
background-color: #da4f49;
border: 1px solid #bd362f;
}
.btn-edit {
color: #fff;
background-color: #7f90ff;
border: 1px solid #2f5cbd;
margin-right: 5px;
}
.btn-danger:hover {
color: #fff;
background-color: #bd362f;
}
.btn:focus {
outline: none;
}
.todo-container {
width: 600px;
margin: 0 auto;
}
.todo-container .todo-wrap {
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
}
</style>
但是,以上代码也是有问题的,比如说失去焦点的问题
2 引出$nextTick的解决方案 - vue是在回调函数全部执行完之后,vue采取执行模板解析
点击编辑按钮,即可获取焦点:
vue,是在回调函数全部执行完之后,vue采取执行模板解析。
为什么直接写无效?因为Vue需要等整个函数走完,才去重新渲染DOM:
1.语法:this.$nextTick( 回调函数 )
2.作用:在下一次DOM更新结束后执行其指定的回调。
3.什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。
<template>
<li>
<label>
<input type="checkbox"
:checked="item.done"
@click="handleClick(item.id)"/>
<span v-show="!item.isEdit">{{ item.title }}</span>
<input type="text"
v-show="item.isEdit"
:value="item.title"
@blur="handleBlur(item, $event)"
ref="inputTitle">
</label>
<button class="btn btn-danger" @click="handleDelete(item.id)">删除</button>
<button v-show="!item.isEdit" class="btn btn-edit" @click="handleEdit(item)">编辑</button>
</li>
</template>
<script>
export default {
name: `UserItem`,
props: ['item'],
methods: {
handleClick(id) {
this.$bus.$emit('checkTodo', id)
},
handleDelete(id) {
if (confirm('确定删除吗?')) {
this.$bus.$emit('deleteTodo', id)
}
},
handleBlur(item, event) {
item.isEdit = false
if (!event.target.value.trim()) {
return alert('输入不能为空 !!!')
} else {
this.$bus.$emit('updateTodoById', item.id, event.target.value.trim())
}
},
handleEdit(item) {
if (item.hasOwnProperty('isEdit')) {
item.isEdit = true
} else {
this.$set(item, 'isEdit', true)
}
this.$nextTick(function(){
this.$refs.inputTitle.focus()
})
},
}
}
</script>
<style scoped>
/* item */
li {
list-style: none;
height: 36px;
line-height: 36px;
padding: 0 5px;
border-bottom: 1px solid #ddd;
}
li label {
float: left;
cursor: pointer;
}
li label li input {
vertical-align: middle;
margin-right: 6px;
position: relative;
top: -1px;
}
li button {
float: right;
display: none;
margin-top: 3px;
}
li:before {
content: initial;
}
li:last-child {
border-bottom: none;
}
li:hover {
background-color: #ddd;
}
li:hover button {
display: block;
}
</style>