TODOS案例
这个案例是要做一个任务清单。
首先 思考一共分为几块内容:
1、输入框
2、任务列表
3、工具栏
因此一共三块,在对应wxml文件中,父容器中添加三个子容器。
<view class="container">
<view class="searchHandle"></view>
<view class="worksHandle"></view>
<view class="utilsHandle"></view>
</view>
对于输入框,又分为左边的添加和后面的输入。
对于任务列表,我们先固定写三个任务去调节样式。
对于工具栏则是三个文本。
在对应wxml中:
<!--index.wxml-->
<view class="container">
<view class="searchHandle">
<view class="addHandle">
<image src="../../images/plus.png"></image>
</view>
<view class="inputHandle">
<input type="text" placeholder="Anything here..."/>
</view>
</view>
<view class="worksHandle">
<view class="item">
<icon type="success"></icon>
<text>学习HTML</text>
<icon type="clear" size="16"></icon>
</view>
<view class="item">
<icon type="success"></icon>
<text>学习CSS</text>
<icon type="clear" size="16"></icon>
</view>
<view class="item">
<icon type="success"></icon>
<text>学习JS</text>
<icon type="clear" size="16"></icon>
</view>
</view>
<view class="utilsHandle">
<text>全选</text>
<text>还剩2个任务</text>
<text>清除任务</text>
</view>
</view>
与之对应的样式:
.container{
border-top: 1rpx solid #e0e0e0;
}
.searchHandle{
display: flex;
align-items: center;
border: 1rpx solid #e0e0e0;
border-radius: 5rpx;
box-shadow: 0 0 5rpx #e0e0e0;
margin: 20rpx;
padding: 20rpx;
}
.addHandle image{
height: 40rpx;
width: 40rpx;
margin-right: 20rpx;
}
.worksHandle{
border: 1rpx solid #e0e0e0;
border-radius: 5rpx;
box-shadow: 0 0 5rpx #e0e0e0;
margin: 20rpx;
}
.worksHandle .item {
display: flex;
padding : 20rpx;
border-bottom: 1rpx solid #e0e0e0;
align-items: center;
}
.worksHandle .item:last-child{
border-bottom: 0;
}
.worksHandle .item text {
flex: 1;
font-size: 30rpx;
color: #444;
margin: 20rpx;
}
.utilsHandle{
margin: 20rpx;
font-size: 30rpx;
display: flex;
justify-content: space-between;
color: #333;
}
效果图:
然后 看看有哪些东西需要被抽象成一个数据成员。
一般来说,对于会变换的东西,一般需要把它抽象出来,写到data中。
对于任务清单,由于是一个列表,所以需要把它抽象成一个数组,根据其中元素个数去改变,数组中每个成员为一个对象。
界面上数据绑定的数据来自data中,data中有什么,这里才可以写什么。
对应js
Page({
data: {
//文本框数据模型
search: '',
//任务清单数据模型
todos: [
//任务名和完成状态
{
name: 'Learning WEAPP',
completed: false
},
{
name: 'Learning JS',
completed: true
},
{
name: 'Learning HTML',
completed: false
}
]
}
})
修改wxml
<!--index.wxml-->
<view class="container">
<view class="searchHandle">
<view class="addHandle">
<image src="../../images/plus.png"></image>
</view>
<view class="inputHandle">
<input type="text" placeholder="Anything here..." value="{
{ search }}" />
</view>
</view>
<view class="worksHandle">
<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="utilsHandle">
<text>全选</text>
<text>还剩{
{todos.length}}个任务</text>
<text>清除任务</text>
</view>
</view>
再然后: 界面交互。1、先让按钮点击时执行一段代码。2、拿到文本框里面的值。由于小程序的数据绑定是单向的,必须要给给文本注册改变事件,通过e.detail.value拿到当前的值。3、将这个值添加到列表中。
Page({
data: {
//文本框数据模型
search: '',
//任务清单数据模型
todos: [
//任务名和完成状态
{
name: 'Learning HTML',
completed: false
},
{
name: 'Learning JS',
completed: true
},
{
name: 'Learning Css',
completed: false
}
],
leftCount: 2,
allCompleted: false
},
addToDoHandle: function () {
//当点击按钮执行的函数,将这个值添加到列表中必须现实的通过setData去改变数据,这样界面上才会得到变化
if (!this.data.search) return
var todos = this.data.todos
todos.push({
name: this.data.search,
completed: false
})
this.setData({
todos: todos,
search: '',
leftCount: this.data.leftCount + 1
})
},
changeHandle: function (e) {
this.setData({
search: e.detail.value
})
},
toggleTodoHandle: function (e) {
//切换当前点中的item的完成状态
var item = this.data.todos[e.currentTarget.dataset.index]
item.completed = !item.completed
//根据当前任务状态决定添加还是减少任务数量
var leftCount = this.data.leftCount //+ (item.completed ? -1 : 1)
if (item.completed) {
leftCount = leftCount - 1;
} else {
leftCount = leftCount + 1;
}
this.setData({
todos: this.data.todos,
leftCount: leftCount
})
},
//需要注意冒泡问题
clearHandle: function (e) {
if (!this.data.todos[e.currentTarget.dataset.index].completed) {
this.data.leftCount = this.data.leftCount - 1;
}
var todos = this.data.todos
todos.splice(e.currentTarget.dataset.index, 1)
//todos中会移除index所指向的元素
this.setData({
todos: todos,
leftCount: this.data.leftCount
})
},
// this在小程序里永远指向的是当前页面对象而不是事件源对象
toggleAllHanale: function () {
var todos = this.data.todos
if (this.data.allCompleted == false) {
for (let index = 0; index < todos.length; index++) {
todos[index].completed = true
}
this.data.allCompleted = true
this.data.leftCount = 0
} else {
for (let index = 0; index < todos.length; index++) {
todos[index].completed = false
}
this.data.allCompleted = false
this.data.leftCount = this.data.todos.length
}
this.setData({
todos: todos,
leftCount: this.data.leftCount
})
},
removeAllCompletedHandle: function () {
//遍历方法:
// var todos = []
// this.data.todos.forEach(function (item) {
// if (!item.completed) {
// todos.push(item)
// }
// })
//filter是过滤,返回值为真添加进新数组,为假不添加
var todos = this.data.todos.filter(function (item){
return !item.completed
})
this.setData({
todos: todos,
})
}
})
对应wxml
其中,value="{
{ search }}"是当前文本框的值,bindconfirm="addToDoHandle"是点击完成添加事件。
<!--index.wxml-->
<view class="container">
<view class="searchHandle">
<view class="addHandle">
<image src="../../images/plus.png" bindtap="addToDoHandle"></image>
</view>
<view class="inputHandle">
<input type="text" placeholder="input ..." value="{
{ search }}" bindinput="changeHandle" bindconfirm="addToDoHandle"/>
</view>
</view>
<!--如果数组长度不为0则显示,否则显示else-->
<block wx:if="{
{todos.length}}">
<view class="worksHandle">
<!--通过三元表达式去控制样式,根据item.completed的取值决定是否添加completed样式。注意:item与花括号之间有个空格-->
<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="clearHandle" data-index="{
{index}}"/>
</view>
</view>
<view class="utilsHandle">
<text bindtap="toggleAllHanale">全选</text>
<!-- 非0就显示 -->
<text wx:if="{
{leftCount}}">还剩{
{leftCount}}个任务</text>
<text bindtap="removeAllCompletedHandle">清除已完成任务</text>
</view>
</block>
<view wx:else class="final">
<text>恭喜你完成所有任务</text>
</view>
</view>
对应wxss
.container {
border-top: 1rpx solid #e0e0e0;
}
.searchHandle {
display: flex;
align-items: center;
border: 1rpx solid #e0e0e0;
border-radius: 5rpx;
box-shadow: 0 0 5rpx #e0e0e0;
margin: 20rpx;
padding: 20rpx;
}
.addHandle image {
height: 40rpx;
width: 40rpx;
margin-right: 20rpx;
}
.worksHandle {
border: 1rpx solid #e0e0e0;
border-radius: 5rpx;
box-shadow: 0 0 5rpx #e0e0e0;
margin: 20rpx;
}
.worksHandle .item {
display: flex;
padding: 20rpx;
border-bottom: 1rpx solid #e0e0e0;
align-items: center;
}
.worksHandle .item:last-child {
border-bottom: 0;
}
.worksHandle .item text {
flex: 1;
font-size: 30rpx;
color: #444;
margin: 20rpx;
}
.worksHandle .item.completed text {
color: #888;
text-decoration: line-through;
}
.utilsHandle {
margin: 20rpx;
font-size: 30rpx;
display: flex;
justify-content: space-between;
color: #333;
}
.final{
height: 800rpx;
display: flex;
justify-content: center;
align-items: center;
}
.final text{
font-size: 50rpx;
color: #888;
}
效果如下:
本次需要提醒注意的事情:
1、block 标签只是为了把两个标签包裹在一起,方便对他们整体进行操作,不会形成任何标签结构。
2、js 中 foreach用法:
数组名.forEach(function(value,index,array) {
//code something });
其中value是必须的,他代表当前数组的每个元素的值。
array[index] == value; //结果为true
java 中
for(元素类型 元素名称 : 遍历数组(集合)(或者能进行迭代的)){ 语句 }
for(String str : arr){
//这里的str就是为了获取每次循环的arr中的值
System.out.println(str); //就相当于 String str=arr[i]
}
3、data-index="{ {index}}"这里的data-index也可以写成其他的,比如data-aaa,但是后面的{ {index}}这个index是系统自己定义的,在这个花括号里只能写系统或者我们自己定义在data中的数据,获取方法就是e.currentTarget.dataset.aaa
结束~