社区/学习指南/小程序云开发学习指南

数据表单

在前面的章节所用到的数据大多都是我们在 js 的 data 里写好的,在这一节里,我们会来介绍如何让用户提交数据。无论是计算器、用户注册、表单收集、发表文章、评论等等,这些都是对用户提交数据的获取。

设置导航栏标题

动态设置导航栏标题是一个非常简单的 API,在技术文档里面可以了解到,只要给 wx.setNavigationBarTitle()的 title 对象赋值,就能改变小程序页面的标题。下面我们会使用多种方法来调用这个 API,既是对前面知识的复习,也让大家了解 API 调用方法有什么不同。

技术文档:wx.setNavigationBarTitle()

onLoad 调用 API

结合前面的知识,我们可以在页面的生命周期函数里来调用 API,使用开发者工具新建一个 form 页面,然后在 form.js 里 onLoad 里添加代码:

onLoad: function (options) {
    wx.setNavigationBarTitle({
      title:"onLoad触发修改的标题"
    })
  },

button 调用 API

我们也还可以通过点击 button 组件,触发事件处理函数来调用 API。在 form.wxml 里输入以下代码

<button type="primary" bindtap="buttonSetTitle">设置标题</button>

然后再在 js 里添加 buttonSetTitle 事件处理函数:

  buttonSetTitle(e){
    console.log(e)
    wx.setNavigationBarTitle({
      title: "button触发修改的标题"
    })
  },

然后点击设置标题,button 就会触发事件处理函数重新给 title 赋值,页面的标题就由“onLoad 触发修改的标题”变成了“button 触发修改的标题”,同时点击组件就会收到一个事件对象,我们把这个事件对象 e 通过 console.log 打印出来发现并没有什么特别有用的信息。这些都是前面我们学过的知识。

使用表单修改标题

那我们如何才能让标题的内容可以根据用户提交的数据进行修改呢?这就涉及到表单的知识啦。小程序一个完整的数据表单收集通常包含一个 form 组件,一个输入框或选择器组件(比如 input 组件),一个 button 组件。

使用开发者工具在 form.wxml 里输入以下代码:

<form bindsubmit="setNaivgationBarTitle">
   <input type="text" placeholder="请输入页面标题并点击设置即可" name="navtitle"></input>
    <button type="primary" formType="submit">设置</button>
</form>

数据表单涉及到的组件多(至少三个),参数以及参数的类型也比较多,上面有几个非常重要的点,大家可以结合上面的代码来理解:

  • 表单最核心的在于表单组件 form,输入框组件 input 和 button 组件要在
    内,form 也会收集内部组件提交的数据;
  • 绑定事件处理函数的不再是 button,而是 form,form 的 bindsubmit 与 button 的 formType="submit"是一对,点击 button,就会执行 bindsubmit 的事件处理函数;
  • input 是输入框,用户可以在里面添加信息;name 是 input 组件的名称,与表单数据一起提交。

在 form.js 里添加事件处理函数 setNaivgationBarTitle,同时我们把事件对象 e 给打印出来:

  setNaivgationBarTitle(e) {
    console.log(e)
    const navtitle = e.detail.value.navtitle
    wx.setNavigationBarTitle({
      title:navtitle
    })
  },

编译之后,在开发者工具的模拟器里输入任意文本,点击“设置”按钮,我们发现导航栏标题都会显示为我们输入的值。在控制台里我们查看一下事件对象。此时的事件对象的 type 属性为 submit(以前的为 tap),我们在 input 输入框填写的值就存储在 detail 对象的 value 属性的 name 名里,这里就是 detail.value.navtitle。

点击 button 组件会执行 form 绑定的事件处理函数 setNaivgationBarTitle,打印事件对象 e,将在 input 输入的值赋值给 navtitle,最后传入 wx.setNavigationBarTitle(),赋值给 title。注意有两个 setNaivgationBarTitle,一个是事件处理函数,一个是 API,前者可以任意命名,后者小程序官方写死不可更改。

对数据表单来说,使用console.log打印事件对象可以让我们对表单提交的数据有一个非常清晰的了解;而使用赋值以及setData可以有效的把表单收集到的数据渲染到页面。

我们也可以把上面的事件处理函数写成如下,让变量 title 与 setNavigationBarTitle 的属性 title 同名,这样 title:title 可以简写成 title。

 setNaivgationBarTitle(e) {
    const title = e.detail.value.navtitle
    wx.setNavigationBarTitle({
      title   //等同于title:title
    })
  },

文本输入框 input

小程序的输入框 input 主要用来处理文本和数字的输入,下面我们就来结合实战与技术文档,来了解一下文本输入框 input 的 type、name、placeholder 等属性。

技术文档:input 技术文档

使用开发者工具在 form.wxml 里输入以下代码,一个 form 组件里面可以包含多个选择器或文本输入框组件,提交数据时,会提交 form 里面填写的所有数据:

<form bindsubmit="inputSubmit">
  <input type="text" name="username" placeholder="请输入你的用户名"></input>
  <input password type="text" name="password" maxlength="6" placeholder="请输入6位数密码"  confirm-type="next" />
  <input type="idcard" name="idcard" placeholder="请输入你的身份证账号" />
  <input type="number" name="age" placeholder="请输入你的年龄" />
  <input type="digit" name="height" placeholder="请输入你身高多少米"/>
  <button form-type="submit">提交</button>
</form>

然后在 form.js 里添加事件处理函数 inputSubmit,主要是为了打印 form 事件对象:

inputSubmit:function(e){
    console.log('提交的数据信息:',e.detail.value)
 },

input 输入框会因为属性的类型的不同,手机键盘外观会有比较大的差异,所以需要点击预览,用微信扫描二维码在手机上体验(也可以启用真机调试)。

  • input 输入框支持的 type 值有文本输入 text、数字输入 number、身份证输入 idcard、小数点输入 digit,当 type 不同时,注意手机键盘外观的不同;
  • placeholder:输入框为空时的占位符(也就是默认值);maxlength:最大输入长度;password 和 disabled 都是 boolean 值,使用方法和之前的 video 组件里面的 boolean 属性一样。

在开发者工具的控制台我们可以看到打印的事件对象里的 value 对象,属性名即为 input 的 name 名,值即为我们输入的数据。如果没有 name。

小任务:给 input 输入框配置 confirm-type,分别输入 send、search、next、go、done,然后点击预览,用微信扫描二维码体验,注意输入内容时,手机键盘显示的不同。

表单组件组合

一个完整的数据收集表单,除了可以提交 input 文本框里面的数据,还可以提交开关选择器按钮 switch、滑动选择器按钮 slider、单选按钮 radio、多选按钮 checkbox 等组件里面的数据。

技术文档:switch 开关选择Slider 滑动选择Radio 单选checkbox 多选form 表单

使用开发者工具在 form.wxml 里添加以下代码,这些组件都是我们日常使用 App、页面等经常会使用到的场景:

<form bindsubmit="formSubmit" bindrest="formReset">
    <view>开关选择器按钮</view>
    <switch name="switch"/>
    <view>滑动选择器按钮slider</view>
    <slider name="process" show-value ></slider>
    <view>文本输入框</view>
    <input name="textinput" placeholder="要输入的文本" />
    <view>单选按钮radio</view>
    <radio-group name="sex">
      <label><radio value="male"/>男</label>
      <label><radio value="female"/>女</label>
    </radio-group>
    <view>多选按钮checkbox</view>
    <checkbox-group name="gamecheck">
      <label><checkbox value="game1"/>王者荣耀</label>
      <label><checkbox value="game2"/>欢乐斗地主</label>
      <label><checkbox value="game3"/>连连看</label>
      <label><checkbox value="game4"/>刺激战场</label>
      <label><checkbox value="game5"/>穿越火线</label>
      <label><checkbox value="game6"/>天天酷跑</label>
    </checkbox-group>
    <button form-type="submit">提交</button>
    <button form-type="reset">重置</button>
</form>

然后在 form.js 里添加 formSubmit 和 formReset 事件处理函数

  formSubmit: function (e) {
    console.log('表单携带的数据为:', e.detail.value)
  },
  formReset: function () {
    console.log('表单重置了')
  }

编译之后,在开发者工具的模拟器里给选择器组件和文本输入组件做出选择以及添加一些值,然后点击提交按钮。在控制台 console,我们可以看到事件对象 e 的 value 对象就记录了我们提交的数据。也就是说,表单组件提交的数据都存储在事件对象 e 的 detail 属性下的 value 里。

  • switch 属性:记录 switch 开关选择的值,这是一个 boolean 值,ture 为开,false 为关;
  • sex 属性:记录 name 名为 sex 的单选按钮的值,它只记录单选选择的那一项的值;
  • process 属性: 记录 name 名为 process 的滑动选择器的值,
  • show-value 为 boolean 值,显示当前 value 值,数据类型为 number;
  • textinput 属性:记录 name 名为 textinput 的 input 文本输入框的值;
  • gamecheck 属性:记录 name 名为 gamecheck 的多选组件的值,数据类型为数组 Array。

点击重置按钮,即会重置表单,并不需要 formReset 事件处理函数做额外的处理。

我们发现上面 button 属性,有时用的是 form-type,有时用的是 formType(注意两者的大小写),这两种写法都可以。我们也可以删掉重置的事件处理函数 formReset,以及 form 组件的 bindreset="formReset",只需要将 button 的 form-type 设置为 reset,也就是
<button form-type="reset">重置</button>
就可以达到重置的效果,绑定事件处理函数 bindreset

只要我们知道 form 表单存储的数据在哪里,就能够结合前面的知识把数据取出来,不同的数据类型区别对待,所以掌握如何使用 JavaScript 操作不同的数据类型很重要。

在技术文档里有这样一句话“当点击 form 表单中 form-type 为 submit 的 button 组件时,会将表单组件中的 value 值进行提交,需要在表单组件中加上 name 来作为 key”。我们也发现 Slider 滑动选择、Radio 单选、checkbox 多选等,都有自己的 value 值,也就是这些组件单独使用时不需要 name 就可以在事件对象的 detail 里取到 value 值,而组合使用时,则必须加 name 才能取到值,大家可以把 name 都取消掉,看看结果如何。

数组的扩展运算符

在这里我们先来介绍一下扩展运算符的概念,它的写法很简单,就是三个点 ...。我们会用案例的方式让大家先了解它的作用,以后会经常用到的。

上面的 gamecheck 记录了我们勾选的多选项的 value 值,它是一个数组 Array。我们可以在 formSubmit 事件处理函数把选项 value 值给打印出来,给上面的 formSubmit 函数添加以下语句:

  formSubmit: function (e) {
    const gamecheck=e.detail.value.gamecheck
    console.log('直接打印的gamecheck',gamecheck)
    console.log('拓展运算符打印的gamecheck',...gamecheck)
  },

然后我们再来填写表单提交数据,从控制台可以看到直接打印 gamecheck,它是一个数组 Array,中括号[ ]就可以看出来,展开也有 index 值;而使用扩展运算符打印 gamecheck,是将数组里的值都遍历了出来。这就是扩展运算符…的作用,大家可以先只了解即可。

添加手机联系人

尽管我们提交了数据,但是当小程序重新编译之后,所有的数据都会被重置,也就是提交的数据并没有保存起来。小程序存储数据有三种方式,一是保存在本地手机上;二是存储到缓存里;三是存储到数据库。下面我们来介绍如何将数据存储到手机。

添加手机通讯录联系人:wx.addPhoneContact()

使用开发者工具在 form.wxml 添加以下代码,注意 input 的 name 名要和 wx.addPhoneContact()里的属性名对应且一致,下面只举几个属性,更多属性都可以按照技术文档添加

<form bindsubmit="submitContact">
  <view>姓氏</view>
  <input name="lastName" />
  <view>名字</view>
  <input name="firstName" />
  <view>手机号</view>
  <input name="mobilePhoneNumbe" />
  <view>微信号</view>
  <input name="weChatNumber" />
  <button type="primary" form-type="submit">创建联系人</button>
  <button type="default" form-type="reset">重置</button>
</form>

然后在 form.js 文件里面输入以下代码,(注意添加手机联系人的 API 在手机上使用有奇效哦)

  submitContact:function(e) {
    const formData = e.detail.value
    wx.addPhoneContact({
      ...formData,
      success() {
        wx.showToast({
          title: '联系人创建成功'
        })
      },
      fail() {
        wx.showToast({
          title: '联系人创建失败'
        })
      }
    })
  },

编译之后,点击开发者工具栏的预览,微信扫描二维码,然后给以上 input 填充数据并点击创建联系人,就可以把数据存储到手机里了。

多写回调函数 success()、fail(),并在里面添加消息提示框 wx.showToast()能够大大增强用户的体验。在编程时多写 console.log,多写回调函数,可以让我们对程序的运行进行诊断,这一点非常重要。不过为了教学方便,我们后面会少写回调函数。

对象的扩展运算符

前面我们已经介绍过数组的拓展运算符,对象的扩展运算符 ...也有类型的作用,它可以取出对象里所有可遍历的属性,拷贝到新的对象中。为了可以看得更加清楚,我们可以进行打印对比:

  submitContact:function(e) {
    const formData = e.detail.value
    console.log('打印formData对象',formData)
    console.log('扩展运算符打印', { ...formData })
  },

尽管打印的结果好像并没有区别,但是 formData 是一个变量,我们把对象赋值给了它,打印它的结果就是一个对象了,而 { ...formData }本身就是一个对象,相当于把 formData 对象里的属性和值给拷贝到了新的对象里面。

小任务:把 wx.addPhoneContact()里的…formData 换成 formData,看看什么结果?把…formData 换成 lastName,又是什么结果?为什么写 lastName 会报错,而写 formData 不会报错?

input 绑定事件处理函数

在 form 表单里,尽管表单里也有 input 组件,但是绑定事件处理函数的是 form 组件,input 组件只提供 value 值,而 input 文本输入组件本身也是可以绑定事件处理函数的。从技术文档里我们了解到 input 可以绑定事件处理函数的属性有:bindinput,键盘输入时触发;bindfocus,输入框聚焦时触发;bindblur,输入框失焦时触发等等,这里主要介绍一下 bindinput。

bindinput 响应式数据渲染

使用开发者工具在 form.wxml 里输入以下代码,这里使用 input 的 bindinput 绑定的事件处理函数 bindKeyInput(函数名可以自己命名),

<view>你输入的是:{{inputValue}}</view>
<input  bindinput="bindKeyInput" placeholder="输入的内容会同步到view中"/>

在 Page 的 data 里我们添加 inputValue 的初始值,

  data: {
    inputValue: '你还没输入内容呢'
  },

编译之后,我们就可以看到 data 里的值渲染到了页面,这是我们前面学过的知识。

我们再在 form.js 里给 input 绑定的事件处理函数 bindKeyInput 添加如下代码(声明一个和 data 里的属性相同的变量名 inputValue,并赋值,setData 可以简写,本节就有了解过哈)

  bindKeyInput: function (e) {
    const inputValue = e.detail.value
    console.log('响应式渲染',e.detail)
    this.setData({
      inputValue
    })
  },

编译之后,我们再在 input 里面填写内容,注意此时我们写的内容会实时渲染到页面上,无论是添加内容还是删除内容,都可以做出同步响应。而在控制台 Console,我们也可以看到每输入/删除一个字符,实时的打印结果,其中 cursor 是 focus 时的光标位置。

注意:回忆一下我们之前的数据渲染,有直接初始化写在 Page 的 data 里,有使用页面生命周期和 button 的方式来触发事件处理函数用 setData 改变数据来渲染,也有 form 表单数据收集,这些数据渲染都没有做到响应式,也就是在不刷新页面的情况下,数据会实时根据你的修改而渲染。

剪贴板

本节前面的添加手机联系人是把收集到的数据存储到本地手机的通讯录里,而剪切板则是把数据存储到本地手机的剪切板里。

技术文档:设置剪切板内容wx.setClipboardData()获取剪切板内容 wx.getClipboardData()

使用开发者工具在 form.wxml 输入以下代码:

<input type="text" name="copytext" value="{{initvalue}}" bindinput="valueChanged"></input>
<input type="text" value="{{pasted}}"></input>
<button type="primary" bindtap="copyText">复制</button>
<button bindtap="pasteText">粘贴</button>

然后在 Page 的 data 里我们添加 initvalue、pasted 的初始值,

  data: {
    initvalue: '填写内容复制',
    pasted: '这里会粘贴复制的内容',
  },

然后在 form.js 中添加 input 绑定的事件处理函数 valueChanged、button 组件绑定的两个事件处理函数 copyText、pasteText:

  valueChanged(e) {
    this.setData({
      initvalue: e.detail.value
    })
  },

  copyText() {
    wx.setClipboardData({
      data: this.data.initvalue,
    })
  },

  pasteText() {
    const self = this
    wx.getClipboardData({
      success(res) {
        self.setData({
          pasted: res.data
        })
      }
    })
  },

在 input 里面输入内容,内容会响应渲染到页面,点击复制按钮,copyText 事件处理函数会调用 API 把数据赋值给剪切板的 data(注意这里的 data 不是 page 页面的 data,是 wx.setClipboardData API 的属性),而点击粘贴按钮,事件处理函数 pasteText 会调用接口,把回调函数 res 里面的数据赋值给 Page 页面 data 里的 pasted,而且页面在没有刷新的情况下实时地把 data 里的 pasted 给渲染了出来。

小任务:上面我们用到的是 input 的 value 属性,将 value 改成 placeholder,对比一下两者有什么不同。前面我们说过,剪切板是把数据存储到了本地手机的剪切板里,使用预览在手机里打开小程序复制内容之后,再到微信聊天界面,使用粘贴看看效果。或者在手机上复制一段内容,然后打开小程序点击粘贴,看看有什么效果。

slider 响应设置颜色

slider 滑动选择器也可以绑定事件处理函数,有:bindchange 完成一次拖动后触发的事件以及 bindchanging 拖动过程中触发的事件。

技术文档:滑动选择器 slider

我们要先回顾一下事件对象里 data-*携带的数据和表单组件携带的数据:首先组件 data-*属性的数据会存储在事件对象里的 currentTarget 下的 dataset 里的属性名里,也就是 data-color 的值会存储在 e.currentTarget.dataset.color 里;而表单组件的数据则是存储在事件对象的 detail 里,也就是 e.detail.value 里。

使用开发者工具在 form.wxml 里输入以下代码,这里会既涉及到 data-*携带的数据,也会涉及到表单组件携带的数据:

<view style="background-color:rgb({{R}},{{G}},{{B}});width:300rpx;height:300rpx"></view>
<slider data-color="R" value='{{R}}' max="255" bindchanging='colorChanging'  show-value>红色</slider>
<slider data-color="G" value='{{G}}' max="255" bindchanging='colorChanging' show-value>绿色</slider>
<slider data-color="B" value='{{B}}' max="255" bindchanging='colorChanging' show-value>蓝色</slider>

然后在 Page 的 data 里我们添加 R、G、B 的初始值(不了解 RGB 颜色值的童鞋可以搜索一下,它们的取值在 0~255 之间),这里的 R、G、B 初始值既是 background-color 的三个颜色的初始值,也是滑动选择器的初始值,我们把它设置为绿色(小程序技术文档的 VI 色)

  data: {
    R:7,
    G:193,
    B:96,
  },

然后在 form.js 里添加 slider 组件绑定的事件处理函数 colorChanging:

  colorChanging(e) {
    console.log(e)
    let color = e.currentTarget.dataset.color
    let value = e.detail.value;
    this.setData({
      [color]: value
    })
  },

编译之后,当我们滑动 slider,view 组件的背景颜色也会随之改变。当滑动 slider 时,colorChanging 因为滑动的拖动会不断触发(类似于英文里的 ing 的状态,实时监听),也就会在控制台 Console 里打印多个值,e.detail.value 为拖动的值,而 e.currentTarget.dataset.color 始终只会有三个结果 R、G、B,而[color]: value 就是把值赋值给 R、G、B 这三个值。

picker 组件

picker 滚动选择器看起来样式非常复杂,不过小程序已经帮我们封装好了,我们只需要用几行简单的代码就可以做一个非常复杂而且类别多样的滚动选择器。

技术文档:滚动选择器 picker

使用开发者工具在 form.wxm 里输入以下代码,只需要下面几行代码,就能从底部弹起一个日期的滚动选择器。而里面的文字可以任意填写,类似于 button、navigator 组件里的字,点击即可执行相应的事件。

<picker mode="date" value="{{pickerdate}}" start="2017-09-01" end="2022-09-01" bindchange="bindDateChange">
选择的日期为:{{pickerdate}}
</picker>
  • mode 属性:滚动选择器有几种模式,不同的模式可以弹出不同类型的滚动选择器,这里的是 date 日期选择,其他模式大体相似;
  • start 和 end 属性:这是日期选择器特有的属性,为有效日期的开始和结束,我们可以滚动试下,超出这个范围就没法滚动了;

然后在 Page 的 data 里我们添加 pickerdate 的初始值

  data: {
    pickerdate:"2019-8-31",
  },

然后在 form.js 中添加 picker 组件绑定的事件处理函数 bindDateChange,我们先打印看看 picker 组件的事件对象:

  bindDateChange: function (e) {
    console.log('picker组件的value', e.detail.value)
  },

编译之后,当我们弹起滚动选择器时,日期选择器默认会指向初始值 2019 年 8 月 31 日,而当我们滑动选择一个日期确定之后,可以在控制台 console 里看到选择的日期。这个日期是一个字符串。

小任务:那我们要如何把选择的日期比如 2019-10-21,从这里取出年月日呢(也就是 2019、10、21)?这个就涉及到字符串的操作了,还记得字符串的操作么?可以看 MDN 技术文档之 JavaScript 标准库之 String,取出具体数字的方法有很多种,你知道应该怎么处理吗?

在这个章节里,我们讲了数据可以存储到本地手机里,在后面的章节,我们还会讲数据存储的其他方式,比如缓存、数据库等。有没有感觉到编程就是逻辑处理、调用 API 和玩弄数据…

本文出自 李东bbsky