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 相关文章推荐
jquery tools之tooltip
Jul 25 Javascript
javascript dom 基本操作小结
Apr 11 Javascript
document.createElement()用法
Mar 13 Javascript
js的onload事件及初始化按钮事件示例代码
Sep 25 Javascript
javascript实现字符串反转的方法
Feb 05 Javascript
JS判断字符串包含的方法
May 05 Javascript
jquery解析json格式数据的方法(对象、字符串)
Nov 24 Javascript
基于百度地图实现产品销售的单位位置查看功能设计与实现
Oct 21 Javascript
javascript实现简易计算器
Feb 01 Javascript
获取layer.open弹出层的返回值方法
Aug 20 Javascript
angularJs中ng-model-options设置数据同步的方法
Sep 30 Javascript
jQuery实现简易QQ聊天框
Feb 10 jQuery
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将部分内容用星号替换
2020/04/21 PHP
php操作redis命令及代码实例大全
2020/11/19 PHP
Javascript条件判断使用小技巧总结
2008/09/08 Javascript
模仿jQuery each函数的链式调用
2009/07/22 Javascript
jQuery文本框(input textare)事件绑定方法教程
2013/04/24 Javascript
jquery mobile的触控点击事件会多次触发问题的解决方法
2014/05/08 Javascript
两种不同的方法实现js对checkbox进行全选和反选
2014/05/13 Javascript
JavaScript字符串对象substr方法入门实例(用于截取字符串)
2014/10/16 Javascript
Javascript定义类(class)的三种方法详解
2015/03/13 Javascript
jQuery的事件委托实例分析
2015/07/15 Javascript
js实现tab切换效果
2017/02/16 Javascript
JavaScript评论点赞功能的实现方法
2017/03/13 Javascript
详解vue渲染函数render的使用
2017/12/12 Javascript
JavaScript模拟实现自由落体效果
2018/08/28 Javascript
NVM安装nodejs的方法实用步骤
2019/01/16 NodeJs
解决vue项目运行提示Warnings while compiling.警告的问题
2020/09/18 Javascript
[01:06] DOTA2英雄背景故事第三期之秩序法则光之守卫
2020/07/07 DOTA
用Python脚本生成Android SALT扰码的方法
2013/09/18 Python
python之yield表达式学习
2014/09/02 Python
python使用xmlrpclib模块实现对百度google的ping功能
2015/06/02 Python
pandas 数据实现行间计算的方法
2018/06/08 Python
tensorflow 用矩阵运算替换for循环 用tf.tile而不写for的方法
2018/07/27 Python
使用python将图片格式转换为ico格式的示例
2018/10/22 Python
Python解决线性代数问题之矩阵的初等变换方法
2018/12/12 Python
python实现图片彩色转化为素描
2019/01/15 Python
python GUI库图形界面开发之PyQt5 Qt Designer工具(Qt设计师)详细使用方法及Designer ui文件转py文件方法
2020/02/26 Python
浅谈python元素如何去重,去重后如何保持原来元素的顺序不变
2020/02/28 Python
python用tkinter实现一个简易能进行随机点名的界面
2020/09/27 Python
Python Django路径配置实现过程解析
2020/11/05 Python
解决python的空格和tab混淆而报错的问题
2021/02/26 Python
Yves Rocher捷克官方网站:植物化妆品的创造者
2019/07/31 全球购物
香港演唱会订票网站:StubHub香港
2019/10/10 全球购物
为什么如下的代码int a=100,b=100;long int c=a * b;不能工作
2013/11/29 面试题
家长给老师的道歉信
2014/01/13 职场文书
电子信息工程专业推荐信
2014/02/14 职场文书
党的群众路线教育实践活动领导班子整改方案
2014/10/25 职场文书