Vue官网todoMVC示例代码


Posted in Javascript onJanuary 29, 2018

这个示例是模仿官网示例样式和功能用我自己的方式写的,基本上没有看官网的源码,只参考自定义指令。让我们一步步来探讨一下。官网demo

要实现的功能

  1. 单条添加todo
  2. 单条删除todo
  3. 双击编辑todo
  4. 单条todo已完成相应样式状态改变
  5. 全部todo是已完成相应样式状态改变
  6. 清除全部已完成todos
  7. 待办todos数量显示
  8. 所有todos,已完成todos,未完成todos筛选

Vue官网todoMVC示例代码

单条添加todo

<input type="text" class="todos_add" placeholder="What needs to be done?" 
@keyup.enter="addTodo($event.target)" //绑定enter事件
ref="currentInput">//操作input元素使enter一下之后清空输入框内容
data() {//一些初始化数据
 return {
  todolists: [],
  dataStatus: ["All", "Active", "Completed"],
  dataStatusIndex: 0,
  whichShow: true,
  defaultShow: true
 }
},
addTodo(e) { //添加todo
 var val = e.value
 if (val === "") {return} //如果输入内容为空则立即返回
 this.todoLists = this.todoLists.concat({//使用concat这个api添加todo
  value: val, //输入内容
  isEditing: false, //是否在编辑状态
  isActive: false, //删除X图标是否激活
  isChecked: false //是否已完成
 })
 this.$refs.currentInput.value = "" //按下enter添加todo之后把输入框value清零
 window.localStorage.setItem("content",JSON.stringify(this.todoLists))//使用localStorage以JSON格式存储数据
},

把每条todo的对应状态都存在同一个对象当中,在操作改变todo状态的时候不会被统一处理,使得每条todo都有单独的状态。

单条删除todo

<li class="content_todoList"
 v-for="(list,index) in todoLists" 
 @mouseover="list.isActive = true" //鼠标移入todo改变对应todo的isActive状态
 @mouseleave="list.isActive=false" //鼠标移出todo改变对应todo的isActive状态
 <span class="el-icon-close content_todoList_delete" 
 :class="{show: list.isActive}" //动态绑定class使鼠标移动到某一todo显示X图标
 @click="deleteTodo(index)"> //绑定删除单条todo事件
 </span>
</li>
deleteTodo(index) { //删除单条todo
  this.todoLists.splice(index, 1)//使用splice的api
  window.localStorage.setItem("content",JSON.stringify(todoLists)) //以JSON格式存储数据//localStorage存储数据
},

在每个li元素上绑定了鼠标移入和移除的事件来动态的改变每个todo的isActive,然后再使用isActive动态显示class。

双击编辑todo&&单条todo已完成

<input type="checkbox" class="checkBox" 
v-model="list.isChecked">//双向绑定isChecked

<div class="content_todoList_main" 
@dblclick="toEdit(list)" //双击事件
v-show="!list.isEditing" //切换元素
:class="{deleted:list.isChecked}"> //动态绑定class该表已完成todo样式
{{list.value}}
</div>

<input type="text" class="content_todoList_main main_input" 
v-model="list.value" //双向绑定可输入value
v-show="list.isEditing" //切换元素
v-todo-focus="list.value" //自定义指令,双击之后自动focus对焦
@blur="unEdit(list)"> //绑定blur失去焦点事件
methods: {
 toEdit(obj) { //使添加的todothing可编辑
  obj.isEditing = true
 },
 
 unEdit(obj) { //使添加的todothing不可编辑
  obj.isEditing = false
 },
}

directives: { //自定义focus指令,需要一个binding参数做判断
 "todo-focus": function (el, binding) {
  if (binding.value) {
   el.focus()
  }
 }
}

通过每个todo里的isEditing属性控制show和是否可编辑两个状态,双击div之后改变当前todo的isEditing为true,从而显示为input,input失去焦点之后再通过blur事件改为false。

通过todo的idChecked来控制是否已完成,从而动态改变样式。

全部todos已完成

<span 
class="icon-down el-icon-arrow-down" //使用element库做向下箭头icon
v-show="todoLists.length>0" //通过todoLists控制是否显示向下箭头icon
@click="selectAllTodos"></span> //全部已完成事件
selectAllTodos() { //设置所有todo为已完成,使用map的api通过todo的isChecked属性操作
 this.todoLists.map(todo => todo.isChecked = todo.isChecked ? false : true)
}

待办todos数量显示

<div class="data_times" v-show="times === 0"> //times为0显示item,大于0显示items,细节注定成败
 <span>{{times}}</span>  item left
</div>
<div class="data_times" v-show="times > 0">
<span>{{times}}</span>  items left</div>
computed: {
 times() { //使用计算属性计算待办todos的次数 
  let todoArr = this.todoLists
  let times = 0
  for (let i = 0; i < todoArr.length; i++) {
   if (todoArr[i].isChecked === false) {
    times++
   }
  }
  return times
 }
},

使用了计算属性对todoLists计算,用for循环刷选出idChecked为true的累加,最后返回times。

清除全部已完成todos

<div class="data_clearTodos" 
@click="clearTodos" 
v-show="times < todoLists.length"> //如果待办事件次数小于总todoLists长度就显示“clear completed”
 <a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >clear completed</a>
</div>

<div class="data_clearTodos" 
@click="clearTodos" 
v-show="times === todoLists.length"> //如果待办事件次数等于总todoLists长度就显示“clear completed”
 <a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" ></a>
</div>
clearTodos() { //清空已完成的todoLists,使用filter的api进行筛选
 this.todoLists = this.todoLists.filter(todo => todo.isChecked === false)
 window.localStorage.setItem("content",JSON.stringify(this.todoLists)) //以json格式存储数据
},

如果待办todos的times小于todoLists长度,就证明有已完成的todo,就可以显示“clear completed”,如果相等就说明没有已完成的todo。

三种状态筛选

所有todos,已完成todos,未完成todos筛选

<li class="content_todoList" 
v-show="defaultShow || (whichShow?list.isChecked:!list.isChecked)">

<div class="data_status">
 <a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" 
 :class="{active: index === dataStatusIndex}" //动态class实现tab切换
 v-for="(item ,index) in dataStatus" v-for循环
 @click="switchStatus(index)" //切换不同tab显示内容
 :key="index">
  {{item}}
 </a>
</div>
switchStatus(index) { //通过if条件判断操作
 this.dataStatusIndex = index
 if (this.dataStatus[index] === "Active") {
  this.defaultShow = false
  this.whichShow = false
 } else if (this.dataStatus[index] === "Completed") {
  this.defaultShow = false
  this.whichShow = true
 } else if (this.dataStatus[index] === "All") {
  this.defaultShow = true
 }
},

我这里是同时if条件句判断操作,有点麻烦,目前还没有想出来其他方案。在li元素使用三元运算符和或运算符进行操作显示不同状态的todos。

完整代码

<style>
 * {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
 }

 input {
  outline: none;
 }

 ul,
 li,
 ol {
  list-style: none;
 }

 #app {
  width: 800px;
  height: 900px;
  margin: 0 auto;
  text-align: center;
  background-color: rgb(245, 245, 245);
  padding: 24px 0;
 }

 #app .header {
  font-size: 48px;
 }

 .content {
  width: 72%;
  margin: 0 auto;
  box-shadow: 0 3px 3px 2px rgba(0, 0, 0, 0.25);
  position: relative;
 }

 .icon-down {
  position: absolute;
  font-size: 24px;
  top: 16px;
  left: 16px;
  cursor: pointer;
 }

 #app .content .todos_add {
  width: 100%;
  height: 56px;
  padding: 16px 56px;
  font-size: 24px;
  border: 1px solid transparent;
 }

 .content_todoLists {
  position: relative;
  z-index: 3;
 }

 .content_todoList {
  display: flex;
  flex-direction: row;
  border-top: 1px solid #ccc;
  font-size: 24px;
  padding: 8px;
  background-color: white;
  align-items: center;
 }

 .checkBox {
  width: 20px;
  height: 20px;
  margin-left: 10px;
 }

 .content_todoList_main {
  flex: 1;
  text-align: left;
  margin-left: 16px;
  font-size: 20px;
  padding: 6px 0;
 }

 .main_input {
  position: relative;
  z-index: 1;
 }

 .content_todoList_delete {
  position: absolute;
  right: 16px;
  color: rgb(252, 55, 55);
  font-weight: 500;
  display: none;
  cursor: pointer;
 }

 .show {
  display: block;
 }

 .deleted {
  text-decoration-line: line-through;
  color: #bbb;
 }

 .show:hover {
  color: rgb(255, 0, 0);
  font-weight: 700;
 }

 ::-moz-placeholder {
  color: rgb(221, 218, 218);
 }

 ::-webkit-input-placeholder {
  color: rgb(221, 218, 218);
 }

 :-ms-input-placeholder {
  color: rgb(221, 218, 218);
 }

 .data {
  display: flex;
  justify-content: space-between;
  padding: 8px;
  font-size: 14px;
  font-weight: 300;
  color: rgb(145, 145, 145);
 }

 a {
  text-decoration: none;
  color: rgb(145, 145, 145);
 }

 .data_times {
  width: 73px;
 }

 .data_clearTodos {
  width: 142px;
 }

 .data_status a {
  display: inline-block;
  border: 1px solid transparent;
  border-radius: 2px;
  padding: 1px 4px;
  margin: 0 2px;
 }

 .data_status a:hover {
  border-color: #bbb;
 }

 .data_clearTodos a:hover {
  text-decoration-line: underline;
 }

 .active {
  box-shadow: 0 0 1px black;
 }
</style>
<body>
 <div id="app">
  <header class="header">todos</header>
  <div class="content">
   <span class="icon-down el-icon-arrow-down" 
   v-show="todoLists.length>0" 
   @click="selectAllTodos">
   </span>
   <input type="text" class="todos_add" placeholder="What needs to be done?" 
   @keyup.enter="addTodo($event.target)" 
   ref="currentInput">
   <ul class="content_todoLists">
    <li v-for="(list,index) in todoLists" class="content_todoList" 
    @mouseover="list.isActive = true" 
    @mouseleave="list.isActive=false"
    v-show="defaultShow || (whichShow?list.isChecked:!list.isChecked)">
     <input type="checkbox" class="checkBox" v-model="list.isChecked">
     <div class="content_todoList_main" @dblclick="toEdit(list)" v-show="!list.isEditing" :class="{deleted:list.isChecked}">
      {{list.value}}
     </div>
     <input type="text" class="content_todoList_main main_input" 
     v-model="list.value" 
     v-show="list.isEditing" 
     v-todo-focus="list.value"
     @blur="unEdit(list)">
     <span class="el-icon-close content_todoList_delete" :class="{show: list.isActive}" @click="deleteTodo(index)"></span>
    </li>
   </ul>
   <div class="data" v-show="todoLists.length>0">
    <div class="data_times" v-show="times === 0">
     <span>{{times}}</span> item left
    </div>
    <div class="data_times" v-show="times > 0">
     <span>{{times}}</span> items left
    </div>
    <div class="data_status">
     <a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" :class="{active:index === dataStatusIndex}" v-for="(item,index) in dataStatus" @click="switchStatus(index)" :key="index">
      {{item}}
     </a>
    </div>
    <div class="data_clearTodos" @click="clearTodos" v-show="times < todoLists.length">
     <a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >clear completed</a>
    </div>
    <div class="data_clearTodos" @click="clearTodos" v-show="times === todoLists.length">
     <a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" ></a>
    </div>
   </div>
  </div>
 </div>
</body>
<script>
 let vm = new Vue({
  el: "#app",
  data() {
   return {
    todoLists: [],
    dataStatus: ["All", "Active", "Completed"],
    dataStatusIndex: 0,
    whichShow: true,
    defaultShow: true
   }
  },
  computed: {
   times() { //使用计算属性计算待办todos的次数 
    let todoArr = this.todoLists
    let times = 0
    for (let i = 0; i < todoArr.length; i++) {
     if (todoArr[i].isChecked === false) {
      times++
     }
    }
    return times
   }
  },
  methods: {
   toEdit(obj) { //使添加的todo可编辑
    obj.isEditing = true
   },
   unEdit(obj) { //使添加的todo不可编辑
    obj.isEditing = false
   },
   addTodo(e) { //添加todo
    var val = e.value
    if (val === "") {
     return
    } //如果输入内容为空则立即返回
    this.todoLists = this.todoLists.concat({ //使用concat这个api添加todo
     value: val, //输入内容
     isEditing: false, //是否在编辑状态
     isActive: false, //删除X图标是否激活
     isChecked: false //是否已完成
    })
    this.$refs.currentInput.value = "" //按下enter添加todo之后把输入框value清零
    window.localStorage.setItem("content", JSON.stringify(this.todoLists)) //使用localStorage以JSON格式存储数据
   },
   deleteTodo(index) { //删除todo
    this.todoLists.splice(index, 1)
    window.localStorage.setItem("content", JSON.stringify(this.todoLists)) //以json格式存储数据
   },
   switchStatus(index) { //试下下方三个状态切换,略麻烦
    this.dataStatusIndex = index
    if (this.dataStatus[index] === "Active") {
     this.defaultShow = false
     this.whichShow = false
    } else if (this.dataStatus[index] === "Completed") {
     this.defaultShow = false
     this.whichShow = true
    } else if (this.dataStatus[index] === "All") {
     this.defaultShow = true
    }
   },
   clearTodos() { //清空已完成的todoLists
    this.todoLists = this.todoLists.filter(todo => todo.isChecked === false)
    window.localStorage.setItem("content", JSON.stringify(this.todoLists)) //以json格式存储数据
   },
   selectAllTodos() { //设置所有todo为已完成
    this.todoLists.map(todo => todo.isChecked = todo.isChecked ? false : true)
   }
  },
  directives: { //自定义focus指令
   "todo-focus": function (el, binding) {
    if (binding.value) {
     el.focus()
    }
   }
  },
  created() {
   let myStorage = window.localStorage.getItem('content')
   this.todoLists = JSON.parse(myStorage) || [] //因为todoLists初始值是null,使用或运算符,如果为null设为空数组
  }
 })
</script>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JS中动态添加事件(绑定事件)的代码
Jan 09 Javascript
js动态添加onload、onresize、onscroll事件(另类方法)
Dec 26 Javascript
jquery之超简单的div显示和隐藏特效demo(分享)
Jul 09 Javascript
js 判断计算字符串长度/判断空的简单方法
Aug 05 Javascript
详解JavaScript中的表单验证
Jun 16 Javascript
关于JS 预解释的相关理解
Jun 28 Javascript
js事件冒泡与事件捕获详解
Feb 20 Javascript
js获取浏览器和屏幕的各种宽度高度
Feb 22 Javascript
jQuery validate 验证radio实例
Mar 01 Javascript
Vue实现搜索 和新闻列表功能简单范例
Mar 16 Javascript
vue自定义指令用法经典实例小结
Mar 16 Javascript
Vue中使用create-keyframe-animation与动画钩子完成复杂动画
Apr 09 Javascript
jquery动态添加以及遍历option并获取特定样式名称的option方法
Jan 29 #jQuery
Angular2 父子组件通信方式的示例
Jan 29 #Javascript
jQuery代码优化方法总结
Jan 29 #jQuery
javascript代码优化的8点总结
Jan 29 #Javascript
浅析Node.js非对称加密方法
Jan 29 #Javascript
360doc网站不登录就无法复制内容的解决方法
Jan 27 #Javascript
使用Vue写一个datepicker的示例
Jan 27 #Javascript
You might like
PHP脚本的10个技巧(3)
2006/10/09 PHP
用PHP实现多级树型菜单
2006/10/09 PHP
PHP gbk环境下json_dencode传送来的汉字
2012/11/13 PHP
PHP中字符与字节的区别及字符串与字节转换示例
2016/10/15 PHP
jQuery-ui中自动完成实现方法
2010/06/10 Javascript
js中的hasOwnProperty和isPrototypeOf方法使用实例
2014/06/06 Javascript
jQuery中triggerHandler()方法用法实例
2015/01/19 Javascript
javascript将数字转换整数金额大写的方法
2015/01/27 Javascript
JS实现文字放大效果的方法
2015/03/03 Javascript
js实现文件上传表单域美化特效
2015/11/02 Javascript
基于JavaScript实现快速转换文本语言(繁体中文和简体中文)
2016/03/07 Javascript
动态JavaScript所造成一些你不知道的危害
2016/09/25 Javascript
js实现动态显示时间效果
2017/03/06 Javascript
AngularJS中使用ngModal模态框实例
2017/05/27 Javascript
详解vue-router和vue-cli以及组件之间的传值
2017/07/04 Javascript
Javascript快速实现浏览器系统通知
2017/08/26 Javascript
基于vue的换肤功能的示例代码
2017/10/10 Javascript
微信小程序事件流原理解析
2019/11/27 Javascript
JS实现页面数据懒加载
2020/02/13 Javascript
如何手写一个简易的 Vuex
2020/10/10 Javascript
Python函数中*args和**kwargs来传递变长参数的用法
2016/01/26 Python
Python编程二分法实现冒泡算法+快速排序代码示例
2018/01/15 Python
Python实现的基于优先等级分配糖果问题算法示例
2018/04/25 Python
Python unittest单元测试框架总结
2018/09/08 Python
实例讲解Python3中abs()函数
2019/02/19 Python
Python图像处理库PIL的ImageFilter模块使用介绍
2020/02/26 Python
PyPDF2读取PDF文件内容保存到本地TXT实例
2020/05/12 Python
原生 JS+CSS+HTML 实现时序图的方法
2019/07/31 HTML / CSS
新西兰最大的天然保健及护肤品网站:HealthPost(直邮中国)
2021/02/13 全球购物
大学辅导员事迹材料
2014/02/05 职场文书
生产部厂长助理职位说明书
2014/03/03 职场文书
办理收楼委托书范本
2014/10/09 职场文书
离婚协议书格式
2014/11/21 职场文书
创业的9条正确思考方式
2019/08/26 职场文书
如何vue使用el-table遍历循环表头和表体数据
2022/04/26 Vue.js
MySQL的意向共享锁、意向排它锁和死锁
2022/07/15 MySQL