TodoList经典案例①

导读:本篇文章讲解 TodoList经典案例①,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

也许你感觉自己的努力总是徒劳无功,但不必怀疑,你每天都离顶点更进一步。今天的你离顶点还遥遥无期。但你通过今天的努力,积蓄了明天勇攀高峰的力量。加油!

案例介绍

如下图所示:
在这里插入图片描述
有几个小细节:

  • 当所有事件都被勾选之后,下面已完成前面的框也会自动勾选

  • 只有在至少一个事件被选中之后,才会出现“清除已完成任务”的按钮
    在这里插入图片描述

  • 当列表中没有待办事件的时候,已完成的那一行整体都不会显示
    在这里插入图片描述

  • 添加的事件出现在列表的最上面

案例要求

使用前端框架Vue实现
且不使用全局事件总线、消息订阅与发布的有关知识

案例分析

首先根据组件化我们肯定是要对这个待办列表进行拆分的。当然这里有很多拆法,我推荐两种:

  • 分成三个部分:
    List item
  • 分成四个部分
    在这里插入图片描述

在这里我使用第二种方式,我们将每一个组件命名:

  • 红色部分组件 – 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. 组件化编码流程:

    ​ (1).拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。

    ​ (2).实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:

    ​ 1).一个组件在用:放在组件自身即可。

    ​ 2). 一些组件在用:放在他们共同的父组件上(状态提升)。

    ​ (3).实现交互:从绑定事件开始。

  2. props适用于:

    ​ (1).父组件 ==> 子组件 通信

    ​ (2).子组件 ==> 父组件 通信(要求父先给子一个函数)

  3. 使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的!

  4. props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/122115.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!