案例介绍
如下图所示:
有几个小细节:
-
当所有事件都被勾选之后,下面已完成前面的框也会自动勾选
-
只有在至少一个事件被选中之后,才会出现“清除已完成任务”的按钮
-
当列表中没有待办事件的时候,已完成的那一行整体都不会显示
-
添加的事件出现在列表的最上面
案例要求
使用前端框架Vue实现
且不使用全局事件总线、消息订阅与发布的有关知识
案例分析
首先根据组件化我们肯定是要对这个待办列表进行拆分的。当然这里有很多拆法,我推荐两种:
- 分成三个部分:
- 分成四个部分
在这里我使用第二种方式,我们将每一个组件命名:
- 红色部分组件 – headers.vue
- 绿色部分组件 – tasks.vue
- 粉色部分组件 – task.vue
- 蓝色部分组件 – record.vue
代码实现
App组件
<template>
<div id="app">
<Headers :addTasks="addTasks" :getLength="getLength"></Headers>
<Tasks :tasks="tasks" :updateTasks="updateTasks" :deleteTask="deleteTask"> </Tasks>
<Record :tasks="tasks" :getFinished="getFinished" :deleteAll="deleteAll" :selectAll="selectAll"></Record>
</div>
</template>
<script>
import Headers from './components/headers'
import Tasks from './components/tasks'
import Record from './components/record'
export default {
name: 'App',
components: {
Headers,
Tasks,
Record
},
data() {
return {
tasks: [
{id: 1, inform: '睡觉', completed: true},
{id: 2, inform: '吃饭', completed: false},
{id: 3, inform: '喝水', completed: true},
//注意此处是示范数据,在实际时候删除,否则可能会报错。按照代码逻辑,如果你删除一个,再添加一个,就会出现
// 两个id为3的数据,那么操作会发生混乱。
//注意:这里的tasks是可以直接进行数据传递的
]
}
},
methods: {
addTasks(task) {
this.tasks.unshift(task)
console.log(this.tasks)
},
getLength() {
return this.tasks.length
},
updateTasks(id) {
for (let i = 0; i < this.tasks.length; i++) {
if (this.tasks[i].id === id) {
this.tasks[i].completed = !this.tasks[i].completed
}
}
},
getFinished() {
let sum = 0
for (let i = 0; i < this.tasks.length; i++) {
if (this.tasks[i].completed == true) {
sum++
}
}
return sum
},
deleteTask(id) {
this.tasks = this.tasks.filter(value => {
return value.id !== id
})
},
deleteAll() {
this.tasks = this.tasks.filter(value => {
return value.completed == false
})
},
selectAll(obj) {
//注意这里要讨论的是按下按钮之后的情况
if (obj.checked == true) {
for (let i = 0; i < this.tasks.length; i++) {
this.tasks[i].completed = true
}
} else {
for (let i = 0; i < this.tasks.length; i++) {
this.tasks[i].completed = false
}
}
},
}
}
</script>
<style>
#app {
width: 400px;
text-align: center;
border-width: 2px;
border-color: #eaeaea;
border-style: solid;
border-radius: 10px;
}
</style>
注意点:
- 考虑到有几个组件可能会使用到tasks数据,所以我们把它放到这些组件的父组件App中,如此便于数据的传递
- tasks里面有几条示范数据。在实际时候删除,否则可能会报错。按照代码逻辑,如果你删除一个,再添加一个,就会出现两个id为3的数据,那么操作会发生混乱。
- 我们使用props来实现父组件向子组件传递数据的功能
- 如果子组件要向父组件传递数据,我们首先要在父组件中定义一个方法,传递给子组件,再让子组件调用即可
- 虽然使用的是data的函数式,但是这里的tasks也是可以直接使用props传递的
- 展示不需要修改的数据直接props就行;如果要改变数据,数据在哪里方法就在哪里
headers组件
<template>
<input type="text" class="read" placeholder="按下Enter添加待办事件" @keydown.enter="addTask" v-model="task">
</template>
<script>
export default {
name: "MyHeader",
props:['addTasks','getLength'],
data(){
return {
task:'',
}
},
methods:{
addTask(){
this.addTasks(
{id:this.getLength()+1,inform:this.task,completed:false}
)
this.task=''
},
}
}
</script>
<style scoped>
.read {
width:370px;
margin-top: 20px;
border: 2px solid #e3e2e2;
border-radius: 10px;
height: 25px;
font-size: 20px;
color: orange;
}
</style>
注意点:
- 在输入完待办事件,按下enter添加之后,输入框要清空
tasks组件
<template>
<div>
<ul id="form" v-for="task in tasks" :key="task.id">
<Task :task="task" :updateTasks="updateTasks" :deleteTask="deleteTask"></Task>
</ul>
</div>
</template>
<script>
import Task from "@/components/task";
export default {
name: "MyTasks",
props:['tasks','updateTasks','deleteTask'],
components:{
Task
}
}
</script>
<style scoped>
#form {
list-style-type: none;
padding: 0px;
}
</style>
注意:
- 使用了v-for的标签是不能当作根标签的,所以这里我使用div再包裹一层
task组件
<template>
<li class="tasks" >
<input type="checkbox" class="select" :checked="task.completed" @click="handleTask(task.id)">{{task.inform}}<button class="delete" @click="deleteOne(task.id)">删除</button>
</li>
</template>
<script>
export default {
name: "MyTask",
props:['task','updateTasks','deleteTask'],
methods:{
handleTask(id){
this.updateTasks(id)
},
deleteOne(id){
this.deleteTask(id)
}
},
}
</script>
<style scoped>
li {
text-align: left;
width: 370px;
margin: 0 auto;
border: 2px solid #e3e2e2;
height: 30px;
line-height: 30px;
}
li:hover {
background-color: #dcdcdc;
visibility: visible;
}
li:hover .delete {
visibility: visible;
}
.delete {
height: 30px;
color: white;
background-color: red;
border-radius: 5px;
visibility: hidden;
float: right;
}
</style>
在做这一部分的时候,难点肯定是怎么作用到具体的元素。怎么理解呢?就是说我们在点击了某一项前面的勾选框之后,我们怎么精确的修改到他的数据?通过id即可,因为我们将点击事件的回调方法放在了App组件中,所以这是一个子组件向父组件传递数据的过程,我们在父组件中定义好方法,并传递给子组件,再由子组件来调用这个方法,在调用的同时传入数据,如此就可以精准的通过id操作到对应的数据。
record组件
<template>
<div class="record" v-show="tasks.length != 0">
<!-- :checked="finish == tasks.length && tasks.length > 0 " 此处是为了列表删除为空之后,取消勾选-->
<input type="checkbox" :checked="getFinished() == tasks.length && tasks.length > 0 " ref="checkAll" @click="selectAll($refs.checkAll)">
已完成{{getFinished()}}/全部{{tasks.length}}
<!-- 展示这种不需要改变的数据直接props就行,如果要改变数据,数据在哪里方法就在哪里-->
<!-- v-show里面如果你这个表达式是一个方法,那么这个方法是要带()的-->
<button class="delete" @click="deleteAllTasks" v-show="ifShow()">清除已完成任务</button>
</div>
</template>
<script>
export default {
name: "MyRecord",
props:['getFinished','deleteAll','selectAll','tasks'],
methods:{
deleteAllTasks(){
this.deleteAll()
this.$refs.fuyuan.checked = false
},
ifShow(){
for (let i = 0; i < this.tasks.length; i++) {
if(this.tasks[i].completed === true) return true
}
return false
}
}
}
</script>
<style scoped>
.record {
width:370px;
margin:0 auto;
text-align: left;
height: 35px;
}
.delete {
height: 30px;
color: white;
background-color: red;
border-radius: 5px;
/*visibility: hidden;*/
float: right;
}
</style>
注意:
- v-show里面如果你这个表达式是一个方法,那么这个方法是要带()的
此处可以不用ref来获取DOM节点,我们也可以使用点击事件对象的target属性来直接获得到这个元素,下面我介绍两种常用的在vue中获取事件对象的方法:
方法①:$event关键字(适用于事件回调函数有外来参数的时候)
方法②:直接写回调函数名(适用于事件回调函数没有外来参数的时候)
案例总结
总结TodoList案例
-
组件化编码流程:
(1).拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。
(2).实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:
1).一个组件在用:放在组件自身即可。
2). 一些组件在用:放在他们共同的父组件上(状态提升)。
(3).实现交互:从绑定事件开始。
-
props适用于:
(1).父组件 ==> 子组件 通信
(2).子组件 ==> 父组件 通信(要求父先给子一个函数)
-
使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的!
-
props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/122115.html