# TodoMvc
**Repository Path**: MonsterLQ/TodoMvc
## Basic Information
- **Project Name**: TodoMvc
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2020-06-15
- **Last Updated**: 2021-01-11
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# TodoMVC
## 数据列表渲染
### 有数据
> 直接渲染
```html
```
### 无数据
> 在没有数据的时候让列表和底部操作隐藏
```javascript
v-if="todos.length"
```
> 这样需要给列表和底部都添加这个指令
> 如果想要只写一边的话,可以在这两个结构外包裹一个盒子,但是这样很不理想,平白无故多出一个盒子,这里`vue`提供了一个标签`template`,这个标签可以搭配`v-if`使用,并且页面不会显示这个标签
```html
```
## 添加任务
### 页面初始化获得焦点
> 在标签中使用`autofocus`属性是不起作用的,这里使用自定义指令,然后在标签中使用`v-focus`即可
```javascript
directives: {
focus: {
inserted: function (el) {
el.focus();
}
}
}
```
### 任务项双击获取焦点
> 需要重新定义一个自定义属性,由于双击后获取焦点,所以就需要把设置的指令放到update中去。
>
> 这样是不严谨的,会执行很多次。所以这里就利用了钩子函数的第二个参数`binding`,这个参数是一个对象,就是利用对象中的`value`属性,这个`value`属性是指令绑定的值,那么就需要使用这个指令的时候绑定值,然后进行判断
```html
v-todo-focus="currentTodo===item"
```
> 这里绑定的值是有意义的,判断点击的是不是当前项,判断的值是布尔值,正好可以用用来value值的判断
```javascript
todoFocus: {
update(el, binding) {
if (binding.value) {
el.focus()
}
}
}
```
> 在这个指令里面做了一个判断,binding.value只有一个是true,然后让当前获取焦点,并且只会执行一次
### 添加任务
> 点击键盘回车添加任务
```html
```
> 事件处理函数
1. 获取文本框数据
2. 不允许有非空数据
3. 添加完成后清空文本框
```javascript
methods:{
handleDown(e) { //e是事件对象
var title = e.target.value.trim(); //获取去除空格后的数据
//添加数据的id,没有数据id为1
var id = this.todos.length ? this.todos[this.todos.length - 1].id + 1 : 1;
if (!title.length) return e.target.value = '' //数据为空不处理
this.todos.unshift({ //把数据添加到todos
id,
title,
completed: false
})
e.target.value = '' //清空文本框
}
}
}
```
> 这里获取数据还可以使用`v-model`数据双向绑定,同时还可以添加修饰符`.trim`-`v-model.trim`可以过滤空白字符
## 任务状态改变
### 单个任务状态改变
> 复选框双向数据绑定
```html
```
> 列表根据数据中的状态添加样式
```html
```
> 复选框是双向数据绑定所以复选框状态改变后,数据也会改变,而列表中是否添加类名就是取决与数据中的状态,这样就达到了复选框状态改变同时列表的样式也会改变
### 全选与全不选
1. 给checked绑定change事件
2. 获取checked的选中状态
3. 遍历所有数据,使状态与checked状态一致
```html
```
```javascript
handleToggleAll(e) {
var checked = e.target.checked //获取选中状态
this.todos.forEach(item => {
item.completed = checked
})
}
```
### 切换联动
> 让全选复选框与任务列表联动
>
> 思路:使用计算属性,用every去未每个todos数据遍历,结果会得到true或false,最后绑定到全选中
```javascript
toggleAll: {
return this.todos.every(item => item.completed)
}
```
```html
:checked="toggleAll"
```
### 优化全选与联动功能
> 这两个功能可以进行优化,合并
>
> 思路:联动的计算属性可以使用双向绑定,而完整版的计算属性中有两个属性get和set两个方法,一个是获取时调用,一个是设置时调用,这两个属性正好对应了当初给全选按钮绑定的change事件,所以在这里可以把change事件取消,把需要操作的写在计算属性的set中
1. 数据双向绑定
2. 在get中书写联动功能
3. 在set中书写全选功能
- 获取当前全选框的状态
- 给所有数据统一设置状态
```javascript
toggleAll: {
get() {
return this.todos.every(item => item.completed)
},
set() {
var checked = !this.toggleAll
this.todos.forEach(item => {
item.completed = checked
})
}
}
```
> 总结:设置联动状态主要利用的就是every这个方法,这个方法会为每个数据都执行操作,全部满足则返回true否则返回false,然后去判断todos里是否都是true,返回的是一个布尔值,把这个值绑定到全选框中就可以达到联动状态。
>
> 在全选功能中,难点就是获取当前全选复选框的选中状态,既然给这个复选框进行了双向数据绑定,那么通过`this.toggleAll`就可以看到当前的状态,但是你要知道这个状态的来源就是计算属性的get方法中计算得来的(可以打印this.toggleAll来观察数据的变化)。在点击时状态就要改变所以那个时候的状态应该取非这才是所需要的状态。
## 任务操作
### 删除单个任务
1. 添加点击删除事件---需要参数id
```html
```
2. 根据对应的参数id删除数据
```javascript
handleDel(id) {
this.todos.forEach((item, i) => {
if (item.id == id) {
this.todos.splice(i, 1)
}
})
}
```
###双击任务项添加编辑样式
> 思路:所有任务项只有当前点击后添加编辑样式.所以当前点击项等于数据的列表项就添加样式
1. 给label添加双击事件
```html