-

在前面的章节所用到的数据大多都是我们在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></form>内,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和玩弄数据…