TODOS案例:
项目介绍
https://github.com/zce/weapp-todos
样式书写
导航栏
效果:
具体代码:
<view class="search"> <image src="../../images/plus.png"></image> <input type="text" placeholder="Anything here..." /> </view>
/* pages/todos/todos.wxss */ .container{ border-top: 1rpx solid #E0E0E0; } .search{ display: flex; align-items: center; margin: 20rpx; padding: 20rpx; border: 1px solid #ccc; border-radius: 5rpx; box-shadow: 0 0 5rpx #E0E0E0; } .search input{ flex: 1; height: 40rpx; } .search image{ width: 40rpx; height: 40rpx; margin-right: 20rpx; }
todos
<view class="todos"> <view class="item"> <icon type="success" /> <text>Learning HTML</text> <icon type="clear" size="16"/> </view> <view class="item"> <icon type="circle" /> <text>Learning CSS</text> <icon type="clear" size="16"/> </view> <view class="item"> <icon type="circle" /> <text>Learning Javascript</text> <icon type="clear" size="16" /> </view> </view>
.todos{ margin: 20rpx; border: 1px solid #ccc; border-radius: 5rpx; box-shadow: 0 0 5rpx #E0E0E0; } .todos .item{ display: flex; align-items: center; padding: 20rpx; border-bottom: 1rpx solid #E0E0E0; /* margin-bottom: -1px; */ } .todos .item:last-child{ border-bottom: 0; } .item text{ flex: 1; margin-left: 20rpx; font-size: 30rpx; color: #444; }
footer
<view class="footers"> <text>Toggle All</text> <text>2 items left</text> <text>Clear completed</text> </view>
.footers{ display: flex; /* 内容对其方式 */ justify-content: space-between; margin: 20rpx; font-size: 30rpx; color: #333; } .footers text{ flex: 1; }
现在整体效果已经产生:
数据绑定
抽象数据模型
// pages/todos/todos.js Page({ /** * 页面的初始数据 */ data: { //文本框数据模型 search: '', //任务清单数据模型 todos: [ {name: 'Learning WEAPP',completed: false}, {name: 'Learning JavaScript',completed: false}, {name: 'Learning HTML',completed: false} ] } })
界面数据绑定
// pages/todos/todos.js Page({ /** * 页面的初始数据 */ data: { //文本框数据模型 search: '', //任务清单数据模型 todos: [ {name: 'Learning WEAPP',completed: false}, {name: 'Learning JavaScript',completed: true}, {name: 'Learning HTML',completed: false} ] } })
<!--pages/todos/todos.wxml--> <view class="container"> <view class="search"> <image src="../../images/plus.png"></image> <input type="text" placeholder="Anything here..." value="{{search}}"/> </view> <view class="todos"> <view class="item{{ item.completed ? ' completed' : '' }}" wx:for="{{ todos}}" > <icon type="{{item.completed? 'success':'circle'}}" /> <text>{{item.name}}</text> <icon type="clear" size="16"/> </view> </view> <view class="footers"> <text>Toggle All</text> <text>{{ todos.length }} items left</text> <text>Clear completed</text> </view> </view>
页面效果:
界面交互操作
新增
目标1: 搜索框中添加的数据,加入到列表当中:
<!--pages/todos/todos.wxml--> <view class="container"> <view class="header"> <image src="../../images/plus.png" bindtap="addTodoHandle"></image> <input type="text" placeholder="Anything here..." value="{{input}}" bindinput="inputChangeHandle"/> </view> <view class="todos"> <view class="item{{ item.completed ? ' completed' : '' }}" wx:for="{{ todos}}" > <icon type="{{item.completed? 'success':'circle'}}" /> <text>{{item.name}}</text> <icon type="clear" size="16"/> </view> </view> <view class="footers"> <text>Toggle All</text> <text>{{ todos.length }} items left</text> <text>Clear completed</text> </view> </view>
// pages/todos/todos.js Page({ /** * 页面的初始数据 */ data: { //文本框数据模型 input: '', //任务清单数据模型 todos: [ {name: 'Learning WEAPP',completed: false}, {name: 'Learning JavaScript',completed: true}, {name: 'Learning HTML',completed: false} ] }, inputChangeHandle: function(e){ //body console.log(e.detail.value); this.setData({ input: e.detail.value }); }, //1.先让按钮点击时 执行一段代码 //2. 拿到文本框里面的值 // 2.1 由于小程序的数据绑定是单向的, // 必须要给文本框注册改变事件 //3. 将这个值添加到列表中 addTodoHandle: function(){ //当添加按钮点击事件发生时执行的函数 console.log(this.data.input) var todos = this.data.todos; todos.push({ name: this.data.input, completed: false }) //必须显式的通过setData去改变数据,这样界面才能得到变化 this.setData({ todos: todos }); } })
效果:
新增逻辑优化
刚才的代码中,如果是input的内容为空,点击+号,还是会添加到todo的列表中:
逻辑优化: 只需要一行代码:
切换任务完成状态
在列表项上添加事件,改变选中项的completed的状态,然后setData赋值即可:
<!--pages/todos/todos.wxml--> <view class="container"> <view class="header"> <image src="../../images/plus.png" bindtap="addTodoHandle"></image> <input type="text" placeholder="Anything here..." value="{{input}}" bindinput="inputChangeHandle"/> </view> <view class="todos"> <view class="item{{ item.completed ? ' completed' : '' }}" wx:for="{{ todos}}" bindtap="toggleTodoHandle" data-index="{{ index }}"> <icon type="{{item.completed? 'success':'circle'}}" /> <text>{{item.name}}</text> <icon type="clear" size="16"/> </view> </view> <view class="footers"> <text>Toggle All</text> <text>{{ todos.length }} items left</text> <text>Clear completed</text> </view> </view>
// pages/todos/todos.js Page({ /** * 页面的初始数据 */ data: { //文本框数据模型 input: '', //任务清单数据模型 todos: [ {name: 'Learning WEAPP',completed: false}, {name: 'Learning JavaScript',completed: true}, {name: 'Learning HTML',completed: false} ] }, inputChangeHandle: function(e){ //body console.log(e.detail.value); this.setData({ input: e.detail.value }); }, //1.先让按钮点击时 执行一段代码 //2. 拿到文本框里面的值 // 2.1 由于小程序的数据绑定是单向的, // 必须要给文本框注册改变事件 //3. 将这个值添加到列表中 addTodoHandle: function(){ S //当添加按钮点击事件发生时执行的函数 if(!this.data.input)return console.log(this.data.input) var todos = this.data.todos; todos.push({ name: this.data.input, completed: false }) //必须显式的通过setData去改变数据,这样界面才能得到变化 this.setData({ todos: todos }); }, toggleTodoHandle:function(e){ //切换当前点中的item的完成状态 console.log(e.currentTarget); var item = this.data.todos[e.currentTarget.dataset.index] console.log(item) item.completed = !item.completed console.log(this.data.todos) this.setData({ todos: this.data.todos }) } })
效果:
剩余任务数量展示
实现目标有3点:
1.若未选中的item的项数大于1,则 应为items left
2.如果为1,则应为item left
3.若没有可选的项,则不显示任务数量
4.注意动态切换任务数量
步骤,主要有3点:
1.定义一个leftCount属性来显示剩余未选项
2.在输入框中的+号点击的时候,添加1
3切换列表的状态时,如果选中的列表项的completed为true则是+1.否则减1
<!--pages/todos/todos.wxml--> <view class="container"> <view class="header"> <image src="../../images/plus.png" bindtap="addTodoHandle"></image> <input type="text" placeholder="Anything here..." value="{{input}}" bindinput="inputChangeHandle"/> </view> <view class="todos"> <view class="item{{ item.completed ? ' completed' : '' }}" wx:for="{{ todos}}" bindtap="toggleTodoHandle" data-index="{{ index }}"> <icon type="{{item.completed? 'success':'circle'}}" /> <text>{{item.name}}</text> <icon type="clear" size="16"/> </view> </view> <view class="footers"> <text>Toggle All</text> <text wx:if="{{ leftCount }}"> {{ leftCount }} {{leftCount > 1 ? 'items' : 'item' }} left</text> <text>Clear completed</text> </view> </view>
// pages/todos/todos.js Page({ /** * 页面的初始数据 */ data: { //文本框数据模型 input: '', //任务清单数据模型 todos: [ {name: 'Learning WEAPP',completed: false}, {name: 'Learning JavaScript',completed: true}, {name: 'Learning HTML',completed: false} ], leftCount: 2 }, inputChangeHandle: function(e){ //body console.log(e.detail.value); this.setData({ input: e.detail.value }); }, //1.先让按钮点击时 执行一段代码 //2. 拿到文本框里面的值 // 2.1 由于小程序的数据绑定是单向的, // 必须要给文本框注册改变事件 //3. 将这个值添加到列表中 addTodoHandle: function(){ //当添加按钮点击事件发生时执行的函数 if(!this.data.input)return console.log(this.data.input) var todos = this.data.todos; todos.push({ name: this.data.input, completed: false }) //必须显式的通过setData去改变数据,这样界面才能得到变化 this.setData({ todos: todos, input:'', leftCount: this.data.leftCount + 1 }); }, toggleTodoHandle:function(e){ //切换当前点中的item的完成状态 console.log(e.currentTarget); var item = this.data.todos[e.currentTarget.dataset.index] console.log(item) item.completed = !item.completed //根据当前任务额完成状态决定增加一个或者减少一个 var leftCount = this.data.leftCount + (item.completed ? -1 : 1) console.log(this.data.todos) this.setData({ todos: this.data.todos, leftCount: leftCount }) } })
删除任务逻辑
注意点:事件冒泡问题,用catchtap事件
<!--pages/todos/todos.wxml--> <view class="container"> <view class="header"> <image src="../../images/plus.png" bindtap="addTodoHandle"></image> <input type="text" placeholder="Anything here..." value="{{input}}" bindinput="inputChangeHandle"/> </view> <view class="todos"> <view class="item{{ item.completed ? ' completed' : '' }}" wx:for="{{ todos}}" bindtap="toggleTodoHandle" data-index="{{ index }}"> <icon type="{{item.completed? 'success':'circle'}}" /> <text>{{item.name}}</text> <icon type="clear" size="16" catchtap="removeToDoHandle" data-index="{{ index }}"/> </view> </view> <view class="footers"> <text>Toggle All</text> <text wx:if="{{ leftCount }}"> {{ leftCount }} {{leftCount > 1 ? 'items' : 'item' }} left</text> <text>Clear completed</text> </view> </view>
// pages/todos/todos.js Page({ /** * 页面的初始数据 */ data: { //文本框数据模型 input: '', //任务清单数据模型 todos: [ {name: 'Learning WEAPP',completed: false}, {name: 'Learning JavaScript',completed: true}, {name: 'Learning HTML',completed: false} ], leftCount: 2 }, inputChangeHandle: function(e){ //body console.log(e.detail.value); this.setData({ input: e.detail.value }); }, //1.先让按钮点击时 执行一段代码 //2. 拿到文本框里面的值 // 2.1 由于小程序的数据绑定是单向的, // 必须要给文本框注册改变事件 //3. 将这个值添加到列表中 addTodoHandle: function(){ //当添加按钮点击事件发生时执行的函数 if(!this.data.input)return console.log(this.data.input) var todos = this.data.todos; todos.push({ name: this.data.input, completed: false }) //必须显式的通过setData去改变数据,这样界面才能得到变化 this.setData({ todos: todos, input:'', leftCount: this.data.leftCount + 1 }); }, toggleTodoHandle:function(e){ //切换当前点中的item的完成状态 console.log(e.currentTarget); var item = this.data.todos[e.currentTarget.dataset.index] console.log(item) item.completed = !item.completed //根据当前任务额完成状态决定增加一个或者减少一个 var leftCount = this.data.leftCount + (item.completed ? -1 : 1) console.log(this.data.todos) this.setData({ todos: this.data.todos, leftCount: leftCount }) }, removeToDoHandle:function(e){ var item = this.data.todos[e.currentTarget.dataset.index] this.data.leftCount = item.completed ? this.data.leftCount : (this.data.leftCount - 1) ; this.data.todos.splice(e.currentTarget.dataset.index, 1) this.setData({ todos: this.data.todos, leftCount: this.data.leftCount }) } })
运行效果:
显示已经删除:
切换全部任务状态(全选)
页面添加一个事件toggleAllHandle
<!--pages/todos/todos.wxml--> <view class="container"> <view class="header"> <image src="../../images/plus.png" bindtap="addTodoHandle"></image> <input type="text" placeholder="Anything here..." value="{{input}}" bindinput="inputChangeHandle"/> </view> <view class="todos"> <view class="item{{ item.completed ? ' completed' : '' }}" wx:for="{{ todos}}" bindtap="toggleTodoHandle" data-index="{{ index }}"> <icon type="{{item.completed? 'success':'circle'}}" /> <text>{{item.name}}</text> <icon type="clear" size="16" catchtap="removeToDoHandle" data-index="{{ index }}"/> </view> </view> <view class="footers"> <text bindtap="toggleAllHandle">Toggle All</text> <text wx:if="{{ leftCount }}"> {{ leftCount }} {{leftCount > 1 ? 'items' : 'item' }} left</text> <text>Clear completed</text> </view> </view>
js书写逻辑:
// pages/todos/todos.js Page({ /** * 页面的初始数据 */ data: { //文本框数据模型 input: '', //任务清单数据模型 todos: [ {name: 'Learning WEAPP',completed: false}, {name: 'Learning JavaScript',completed: true}, {name: 'Learning HTML',completed: false} ], leftCount: 2, allCompleted: false }, inputChangeHandle: function(e){ //body console.log(e.detail.value); this.setData({ input: e.detail.value }); }, //1.先让按钮点击时 执行一段代码 //2. 拿到文本框里面的值 // 2.1 由于小程序的数据绑定是单向的, // 必须要给文本框注册改变事件 //3. 将这个值添加到列表中 addTodoHandle: function(){ //当添加按钮点击事件发生时执行的函数 if(!this.data.input)return console.log(this.data.input) var todos = this.data.todos; todos.push({ name: this.data.input, completed: false }) //必须显式的通过setData去改变数据,这样界面才能得到变化 this.setData({ todos: todos, input:'', leftCount: this.data.leftCount + 1 }); }, toggleTodoHandle:function(e){ //切换当前点中的item的完成状态 console.log(e.currentTarget); var item = this.data.todos[e.currentTarget.dataset.index] console.log(item) item.completed = !item.completed //根据当前任务额完成状态决定增加一个或者减少一个 var leftCount = this.data.leftCount + (item.completed ? -1 : 1) console.log(this.data.todos) this.setData({ todos: this.data.todos, leftCount: leftCount }) }, removeToDoHandle:function(e){ var item = this.data.todos[e.currentTarget.dataset.index] this.data.leftCount = item.completed ? this.data.leftCount : (this.data.leftCount - 1) ; this.data.todos.splice(e.currentTarget.dataset.index, 1) this.setData({ todos: this.data.todos, leftCount: this.data.leftCount }) }, toggleAllHandle: function(){ //this在这里永远指向的是当前页面对象 console.log(11) this.data.allCompleted = !this.data.allCompleted var todos = this.data.todos var that = this todos.forEach(function(item){ item.completed = that.data.allCompleted }) this.setData({ todos: this.data.todos, leftCount: this.data.allCompleted? 0 : todos.length }) } })
效果:
全选:
全不选:
清空已完成任务
在页面定义个clearHandle方法:
<!--pages/todos/todos.wxml--> <view class="container"> <view class="header"> <image src="../../images/plus.png" bindtap="addTodoHandle"></image> <input type="text" placeholder="Anything here..." value="{{input}}" bindinput="inputChangeHandle"/> </view> <view class="todos"> <view class="item{{ item.completed ? ' completed' : '' }}" wx:for="{{ todos}}" bindtap="toggleTodoHandle" data-index="{{ index }}"> <icon type="{{item.completed? 'success':'circle'}}" /> <text>{{item.name}}</text> <icon type="clear" size="16" catchtap="removeToDoHandle" data-index="{{ index }}"/> </view> </view> <view class="footers"> <text bindtap="toggleAllHandle">Toggle All</text> <text wx:if="{{ leftCount }}"> {{ leftCount }} {{leftCount > 1 ? 'items' : 'item' }} left</text> <text bindtap="clearHandle">Clear completed</text> </view> </view>
// pages/todos/todos.js Page({ /** * 页面的初始数据 */ data: { //文本框数据模型 input: '', //任务清单数据模型 todos: [ {name: 'Learning WEAPP',completed: false}, {name: 'Learning JavaScript',completed: true}, {name: 'Learning HTML',completed: false} ], leftCount: 2, allCompleted: false }, inputChangeHandle: function(e){ //body console.log(e.detail.value); this.setData({ input: e.detail.value }); }, //1.先让按钮点击时 执行一段代码 //2. 拿到文本框里面的值 // 2.1 由于小程序的数据绑定是单向的, // 必须要给文本框注册改变事件 //3. 将这个值添加到列表中 addTodoHandle: function(){ //当添加按钮点击事件发生时执行的函数 if(!this.data.input)return console.log(this.data.input) var todos = this.data.todos; todos.push({ name: this.data.input, completed: false }) //必须显式的通过setData去改变数据,这样界面才能得到变化 this.setData({ todos: todos, input:'', leftCount: this.data.leftCount + 1 }); }, toggleTodoHandle:function(e){ //切换当前点中的item的完成状态 console.log(e.currentTarget); var item = this.data.todos[e.currentTarget.dataset.index] console.log(item) item.completed = !item.completed //根据当前任务额完成状态决定增加一个或者减少一个 var leftCount = this.data.leftCount + (item.completed ? -1 : 1) console.log(this.data.todos) this.setData({ todos: this.data.todos, leftCount: leftCount }) }, removeToDoHandle:function(e){ var item = this.data.todos[e.currentTarget.dataset.index] this.data.leftCount = item.completed ? this.data.leftCount : (this.data.leftCount - 1) ; this.data.todos.splice(e.currentTarget.dataset.index, 1) this.setData({ todos: this.data.todos, leftCount: this.data.leftCount }) }, toggleAllHandle: function(){ //this在这里永远指向的是当前页面对象 console.log(11) this.data.allCompleted = !this.data.allCompleted var todos = this.data.todos var that = this todos.forEach(function(item){ item.completed = that.data.allCompleted }) this.setData({ todos: this.data.todos, leftCount: this.data.allCompleted? 0 : todos.length }) }, clearHandle:function(){ var todos = this.data.todos.filter(function(item){ return !item.completed }) this.setData({todos: todos}) } })
效果:
点击clear Completed,只显示未完成的 任务:
细节优化和总结
1:输入框中输入完成后,点击回车,就会添加
2:所有任务被清空后,不应该呈现列表和按钮
bindconfirm事件
第一个问题的处理: bindconfirm事件
呈现问题–block
根据todos有没有数据,有数据就呈现todo列表和footer,没有就不显示
<!--pages/todos/todos.wxml--> <view class="container"> <view class="header"> <image src="../../images/plus.png" bindtap="addTodoHandle"></image> <input type="text" placeholder="Anything here..." value="{{input}}" bindinput="inputChangeHandle" bindconfirm="addTodoHandle"/> </view> <block wx:if="{{ todos.length }}"> <view class="todos"> <view class="item{{ item.completed ? ' completed' : '' }}" wx:for="{{ todos}}" wx:key="{{ index }}" bindtap="toggleTodoHandle" data-index="{{ index }}"> <icon type="{{item.completed? 'success':'circle'}}" /> <text>{{item.name}}</text> <icon type="clear" size="16" catchtap="removeToDoHandle" data-index="{{ index }}"/> </view> </view> <view class="footers"> <text bindtap="toggleAllHandle">Toggle All</text> <text wx:if="{{ leftCount }}"> {{ leftCount }} {{leftCount > 1 ? 'items' : 'item' }} left</text> <text bindtap="clearHandle">Clear completed</text> </view> </block> <view wx:else> <text>null</text> </view> </view>
看下效果:
会发现内容不见了,显示null,大功告成!
真机演示
点击预览:
出现个二维码,扫码就可在手机上看到效果.
完