小程序自定义搜索选择组件

3,642 阅读4分钟

引言

在开发中经常遇到需要搜素之后在返回的列表中再选择的需求,能便利在大量数据中的选择行为,有一些已有的组件例如select2.js、iview的AutoComplete组件等等。由于小程序没有类似的组件,只能自己写一个,其实比较基础,欢迎阅读。

分析思路

思路是这样的:

  • 为了适应各种返回格式不同的接口,组件应该直接接收一个数组,数据请求最好由父页面完成,后台返回的数据可能不是字符串数组,更有可能是对象数组,因此还要有参数指定显示的属性名;
  • 搜索却是在组件中进行的,所以搜索内容要传给父级页面作为接口参数,接口需要做节流;
  • 选中后将选中的下标返回父页,然后执行回调,因为很多选中后还会联动其余值的填入,其他数据一般也在选中的对象中,在父页面用下标直接从数组中获取就可以了;

效果图如下,截图是开发工具里的,手机上滚动条没这么丑^ ^

组件编写

组件名为search-picker,在开发工具右键新建Components,由于组件内及父页面都能控制组件的显示隐藏(组件内是选中后直接隐藏),所以隐藏状态要保持一致,否则会出现父页面触发了却不显示的问题。

1、组件的js

//components/search-picker/search-picker.js
Component({
    properties: {
        list:{
            type:Array,
            value:[]
        },//可选列表
        placeholder:{
            type:String,
            value:'请输入内容进行选择'
        },
        key:{
            type:String,
            value:'title'
        },//列表显示的字段名,默认是title
        show:{
            type:Boolean,
            value:false
        },//是否显示组件
    },
    data: {
        inputVal:'',//输入内容
    },
    methods: {
        //获取组件输入的内容传递给父页面
        getInput: function (e) {
            this.setData({
                inputVal:e.detail.value
            });
            this.triggerEvent('getInput', this.data.inputVal);
        },
        //选中结果后,将下标传递给父页面,并隐藏组件
        select: function (e) {
            let index = e.currentTarget.dataset.index;
            let key = this.data.key;
            this.setData({
                show:false
            });
            this.triggerEvent('getSelect',index)
        },
        //清空输入
        clearSelect: function () {
            this.setData({
                inputVal:'',
            });
        },
        //隐藏组件,必须将隐藏状态传递给父页面,因为父页面也有控制隐藏显示的属性,不传递的话,该属性还是true
        hidePicker: function () {
            this.setData({
                show:false,
            });
            this.triggerEvent('showFn',false)
        }
    }
});

2、组件的wxml

<!--components/search-picker/search-picker.wxml-->
<!--搜索选择组件,父级包含组件的元素要相对定位,否则组件无法定位-->
<view class="searchPicker" hidden="{{!show}}">
    <view class="inputView">
        <input type="text"
               value="{{inputVal}}"
               placeholder="{{placeholder}}"
               placeholder-class="placeholder"
               bindinput="getInput"
        />
        <!--清空输入-->
        <icon type="clear" size="18" hidden="{{inputVal==''}}" bindtap="clearSelect"></icon>
    </view>
    <scroll-view class="list" scroll-y>
        <!--有结果显示列表-->
        <block wx:if="{{list.length!=0}}">
            <view wx:for="{{list}}"
                  wx:key="{{index}}"
                  class="item"
                  bindtap="select"
                  data-index="{{index}}">
                  <!--如果没有传key,说明是字符串数组,直接访问item-->
                <text class="item">{{item[key]||item}}</text>
            </view>
            <text class="tips">没有想要的结果?请输入更精确的关键字~</text>
        </block>
        <!--没有结果-->
        <text wx:if="{{inputVal!='' && list.length==0}}" class="tips">抱歉,没有结果哦~</text>
    </scroll-view>

</view>
<!--用来点击组件外部隐藏-->
<view class="closePicker" hidden="{{!show}}" bindtap="hidePicker"></view>

组件使用

1、使用页面的json中要声明

"usingComponents": {
    "search-picker":"/components/search-picker/search-picker"
}

2、组件标签的使用

<!--组件使用页面.wxml-->
<view class="formItem" style="position: relative">
    <text class="itemTitle">**名称<text class="red">*</text></text>
    <input type="text" disabled value="{{clientname}}" bindtap="showPickerFn" 
    placeholder="请选择**名称" placeholder-class="placeholder"/>
    <search-picker
            placeholder="请输入名称进行选择"
            key="{{clientKey}}"
            list="{{clientList}}"
            show="{{showSearch}}"
            bindgetInput="getInput"
            bindgetSelect="getSelect"
            bindshowFn="showFn"
            class="searchPicker"
    ></search-picker>
</view>

3、组件所需参数及回调

//组件使用页面.js
...
data:{
    clientname: '',//选中名称
    clientList: [],//机构可选列表
    showSearch:false,//是否显示
    clientKey:'name',//列表中显示的字段名
    timeout:'',//计时器
    ...
},
//显示搜索选择框
showPickerFn:function(){
    this.setData({
        showSearch:!this.data.showSearch
    });
    //可以在这里提前传空值请求一次列表,可返回一个初始的列表
    this.getClientList('');
},
//获取组件中输入的内容
getInput: function(e){
    let that = this;
    //    若已经有定时任务,将其清除,重新定义新的计时器,在输入长时间停顿之前不请求接口
    if(this.data.timeout!=''){
        clearTimeout(this.data.timeout);
    }
    let timeout = setTimeout(function () {
        that.getClientList(e.detail)
    },800);
    this.setData({
        timeout:timeout
    });
},
//根据输入的内容请求数据,搜索数据列表
getClientList: function(key){
    let that = this;
    <!--这里是项目里封装好的wx.request,可直接用wx.request-->
    app.ajax({
        method:'post',
        url:'/url/clients',
        data:{
            key:key
        }
    }).then(resp=>{
        that.setData({
            clientList:resp.list,
        })
    });

},
//将组件选中的项回填
getSelect: function(e){
    let index = e.detail;
    let that = this;
    this.setData({
        clientname:that.data.clientList[index].title,
        clientid:that.data.clientList[index].id,
        showSearch:false
    })
    //把选中的数据重置为空
    this.resetData();
    //根据id做其他数据请求
    this.getCompany(that.data.clientList[index].id);
    ···
},
// 隐藏回调,即组件中隐藏了组件后,父级也要同步
showFn: function(e){
    this.setData({
        showSearch:e.detail,
    })
},
···

样式就不粘了,需要的话可以去github。 以上就是全部啦,有问题的话,欢迎指正~