接下来就是筛选功能,有了之前筛选全部未完成的 todo 的经验,相信实现其它的筛选思路也就非常清晰了,我们这里有三种筛选:
- 全部:显示全部 todo
- 进行中:显示未完成的 todo
- 已完成:显示已完成的 todo
为了增强用户体验,选中的按钮应该高亮显示,我们这里把它标红了。
现在唯一的问题是,我们如何知道应该筛选出哪中类型的 todo 呢?用户是想要进行中的 todo 还是已完成的todo?可以发现用户会点击相应的按钮来表明他的意图。如果我们在用户点击按钮时把他的意图传递给 Vue 对象,Vue 就知道该怎么做了。
因此我们给 data
增加一个属性 intention
,来记录用户的意图。我们定义三种意图:
- all:想查看全部 todo
- ongoing:想查看未完成的 todo
- completed:想查看已完成的 todo
var app = new Vue({
el: '#todo-app',
data: function () {
return {
todos: [],
newTodoTitle: '',
editedTodo: null,
intention: 'all', // 默认为 all
}
},
...
})
然后我们需要根据用户的意图从 todos 中筛选 todo,之前说过,从 Vue 已绑定的数据中计算新的结果是计算属性的典型应用场景,所以我们加一个 filteredTodos
计算属性:
computed: {
leftTodosCount: function () {
return this.todos.filter(todo => !todo.finished).length
},
filteredTodos: function () {
if (this.intention === 'ongoing') {
return this.todos.filter(todo => !todo.finished)
} else if (this.intention === 'finished') {
return this.todos.filter(todo => todo.finished)
} else {
// 其它未定义的意图我们为其返回全部 todos,
// 这里面已经包含了 all 意图了
return this.todos
}
},
},
当然我们会发现未完成的 todo 分别在 filteredTodos
和 leftTodosCount
两个计算属性中被计算了两次,为了优化一下计算效率,我们可以重构一下代码:
computed: {
leftTodos: function () {
return this.todos.filter(todo => !todo.finished)
},
leftTodosCount: function () {
return this.leftTodos.length
},
filteredTodos: function () {
if (this.flag === 'ongoing') {
return this.leftTodos
} else if (this.flag === 'finished') {
return this.todos.filter(todo => todo.finished)
} else {
// 其它未定义的意图我们为其返回全部 todos,
// 这里面已经包含了 all 意图了
return this.todos
}
}
},
filteredTodos
就是我们根据用户意图返回的结果。
打开浏览器,刷新看看效果!发现怎么点击筛选按钮返回的还是全部 todo。好吧,我想你也想到了,我么只是在 vue 中定义了计算属性,但是用户点击按钮并没有把它们的意图传给 Vue 对象。我们来设置一下,让用户的意图在点击按钮时传给 Vue,这样 Vue 才知道如何操作。给各个筛选按钮绑定一个 click 事件。
<div>
<span>剩余<span style="font-weight: bold;"> {{leftTodosCount}} </span>项未完成 ---</span>
<span>筛选:
<input type="button" value="全部"
class="selected"
@click="intention='all'"/>
<input type="button" value="进行中"
@click="intention='ongoing'"/>
<input type="button" value="已完成"
@click="intention='finished'"/>
<input type="button" value="清除已完成">
<input type="button" value="清除全部">
</span>
</div>
特别注意不要忘了 "intention='all'" all 两边的引号,因为这是一个字符串。
再次打开浏览器看效果,好吧,怎么点还是没反应。忘了刷新?刷新一下,还是没反应!哪里出错了?F12 打开控制台调试,Vue 显示欢迎消息没有任何错误提示!!
尝试着自己找找原因~~
我们之前在循环显示 todo 列表时使用的是 this.todos
的数据,当然你无论如何点击按钮循环的始终都是 todos 的数据。把循环的内容改为 filteredTodos
。
<li v-for='todo in filteredTodos' :key='todo.id'>
<span :class="{finished: todo.finished}"
@dblclick="editTodo(todo)">{{ todo.title }}</span>
...
</li>
大功告成!
练习
现在筛选的功能基本完成了,但是有一个地方还没有实现,用户点击相应意图的按钮,对应的按钮应该高亮显示,而现在始终只有全部按钮高亮,因为我们在 html 中为其设置了 class
始终为 selected
。尝试修改代码实现需求。(hint:别忘了 Vue 的动态样式绑定。如何判断用户点击了哪个按钮呢?)
-- EOF --
关于"错误",应该是作者特意留的bug 哈哈.
首先,按了那三个按钮,改变的是this.intention,但是在filteredTodos函数里面看的是this.flag;
然后就是"finished"和"completed"在哪个地方混淆了...
``` filteredTodos: function () {
if (this.intention === "ongoing") {
return this.leftTodos
} else if (this.intention === "completed") {
return this.todos.filter(todo => todo.completed)
} else {
// 其它未定义的意图我们为其返回全部 todos,
// 这里面已经包含了 all 意图了
return this.todos
}
}
```
这里的finished是不是应该是completed,跟前面文章对应~