阅读量:0
To Do List案例
MyFooter.vue
<template> <!--隐式类型转换--> <div class="todo-footer" v-show="total"> <label> <!--这里也可用v-model来替代,此时不需要计算属性了--> <!-- <input type="checkbox" :checked="isAll" @change="checkAll"/>--> <input type="checkbox" v-model="isAll"/> </label> <span> <span>已完成{{ doneTotal }}</span> / 全部{{total}} </span> <button class="btn btn-danger" @click="clearAll">清除已完成任务</button> </div> </template> <script> export default { name: "MyFooter", props: ['todos', 'checkAllTodo', 'clearAllDoneTodo'], computed:{ total(){ return this.todos.length; }, doneTotal(){ return this.todos.reduce((todoTotal, todo) => { //隐士类型转换 return todoTotal + todo.done; }, 0); // return this.todos.filter(todo => todo.done).length; }, isAll:{ get(){ return this.total === this.doneTotal && this.doneTotal > 0; //计算属性可以通过其他的计算属性接着进行计算得到结果 }, set(value){ //value注意要么为true,要么为false,因为你是把它应用在了checkbox上 this.checkAllTodo(value); } } }, methods:{ // checkAll(e){ // // console.log(e.target.checked); //判断这个checkbox到底是不是全选 true全选 false全不选 // this.checkAllTodo(e.target.checked); // } clearAll(){ this.clearAllDoneTodo(); } } } </script> <style scoped> /*footer*/ .todo-footer { height: 40px; line-height: 40px; padding-left: 6px; margin-top: 5px; } .todo-footer label { display: inline-block; margin-right: 20px; cursor: pointer; } .todo-footer label input { position: relative; top: -1px; vertical-align: middle; margin-right: 5px; } .todo-footer button { float: right; margin-top: 5px; } </style>
MyHeader.vue
<template> <div class="todo-header"> <input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="title" @keyup.enter="add"/> </div> </template> <script> import { nanoid } from 'nanoid'; export default { //注意不管是你写的data也还还是methods也好,甚至是computed计算属性也好都会出现在组件事例对象vc身上 //属性值不能重名 name: "MyHeader", data(){ return { title: '' } }, methods:{ add(){ //将用户的输入包装成一个todo对象 console.log(this.title) if(!this.title.trim()) {//这个判断的意思就是,如果输入框为空,就跳出这个函数,为什么要取反?因为字符串转为布尔值就是false,所以得取反为true,跳出函数。 //意思就是如果传过来的todo的title也就是输入的值为空的话,就return跳出函数了 alert('代办事项不能为空') return; //输入的代办事项为空则不走下面流程 } const todoObj = { id: nanoid(),//简单的库,比uuid快和轻便很多 title: this.title, done:false }//这是兄弟组件,不是父子组件,传数据失败,header和list不互通 //props一般是单向通信,即父传子,官方不建议子传父,会导致数据流向难以理解。 // // console.log(todoObj); this.addTodo(todoObj) this.title = ''; } }, props:['addTodo'], } </script> <style scoped> /*header*/ .todo-header input { width: 560px; height: 28px; font-size: 14px; border: 1px solid #ccc; border-radius: 4px; padding: 4px 7px; } .todo-header input:focus { outline: none; border-color: rgba(82, 168, 236, 0.8); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); } </style>
Item.vue
<template> <li> <label> <!-- 这里勾选和取消勾选可以使用change和click作为事件处理--> <input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/> <!--v-model数据的双向绑定,checkbox使用v-model来双向绑定其是否被勾选,也可以实现效果但不推荐(因为其实修改了props中的数据)--> <!--这里修改了从List修改过来的props,这里的不允许改是浅层次,就是如果props是一个对象则这个修改这个对象的某一个属性vue是放行的--> <!-- <input type="checkbox" v-model="todo.done"/>--> <!-- 这里时props传入的数据,不宜使用v-model,因为不允许在子组件中修改父组件的数据 --> <span>{{ todo.title }}</span> <!--@change 原型 是js里面的onchange 当input表单value发生改变触发事件--> </label> <button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button> </li> </template> <script> export default { name: "Item", //声明接收todo props: ['todo', 'checkTodo', 'deleteTodo'], methods:{ handleCheck(id){ this.checkTodo(id); }, handleDelete(id){ if(confirm(`确定删除编号为${id}的todo吗`)){ // console.log(id); this.deleteTodo(id); } } } } </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: #ddd; } li:hover button{ display: block; } </style>
List.vue
<template> <ul class="todo-main"> <Item v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj" :checkTodo="checkTodo" :deleteTodo="deleteTodo" /> </ul> </template> <script> import Item from "@/components/Item"; export default { name: "List", components: { Item, }, props:['todos', 'checkTodo', 'deleteTodo'] } </script> <style scoped> /*main*/ .todo-main { margin-left: 0; border: 1px solid #ddd; border-radius: 2px; padding: 0px; } .todo-empty { height: 40px; line-height: 40px; border: 1px solid #ddd; border-radius: 2px; padding-left: 5px; margin-top: 10px; } </style>
App.vue
<template> <div id="root"> <div class="todo-container"> <div class="todo-wrap"> <MyHeader :addTodo="addTodo"/> <List :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo" /> <MyFooter :todos="todos" :checkAllTodo="checkAllTodo" :clearAllDoneTodo="clearAllDoneTodo" /> </div> </div> </div> </template> <script> import MyHeader from "@/components/MyHeader"; import List from "@/components/List"; import MyFooter from '@/components/MyFooter'; export default { name: "App", components:{ List, MyFooter, MyHeader }, data() { return { todos: [ {id: '001', title: '吃饭', done: false}, {id: '002', title: "睡觉", done: true}, {id: '003', title: '打代码', done: false} ] } }, methods:{ //添加的todo addTodo(todo){ console.log('我是app组件,我收到了数据'); this.todos.unshift(todo); }, checkTodo(id){ const todo = this.todos.find(todo => todo.id === id); todo.done = !todo.done; }, deleteTodo(id){ this.todos = this.todos.filter(todo => todo.id !== id); }, checkAllTodo(done){ this.todos.forEach(todo => todo.done = done); }, clearAllDoneTodo(){ this.todos = this.todos.filter(todo => !todo.done) } } } </script> <style> /*base*/ body { background: #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-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; } /*别人的项目可不这么这么写,CSS写到后面,发现前面的可能要加点什么直接后面补了,真是离了大谱*/ </style>
全都勾选了有可能是MyList里面的done后面写成了字符串,,把单引号或者双引号去掉就行了
因为你们todoList里面是“true”而不是true,done 要写布尔值,不要写字符串
父组件定义函数,子组件调用函数传递参数。子组件定义属性参数列表,父组件调用子标签属性传参。这里的this还是header,只不过header和app里的received方法引用地址相同,你在header里面调用了,也相当于在app中调用了
这里this都是 实例对象vc 函数是地址值 不是传进来个全新的对象,在js中,函数也是对象,传函数本身实际上是引用传递,意思他们两用的是同一块内存空间,一方修改,另一方也会改变
header里并没有修改任何值,只是传了个参数回app,app那个this才真正修改了vc上的数据
072_尚硅谷Vue技术_TodoList案例_添加_哔哩哔哩_bilibili 这5分钟很好
关于addtodo,这里是按下回车后调用的add方法与APP传过来的add方法重名了
勾选
是非常重要,Vue将页面拆分成各种组件,组件间就得通信拿数据,如果不用Vue,就一个页面,数据都在一起那还用啥通信了
@change="todoObj.done=!todoObj.done" 就可以了
v-model底层就是监听checked 绑定change