select2,利用ajax高效查询大数据列表(可搜索、可分页)

简介: 版权声明:欢迎转载,请注明沉默王二原创。 https://blog.csdn.net/qing_gee/article/details/72519313 select2是一款jquery插件,是普通form表单select组件的升级版。
版权声明:欢迎转载,请注明沉默王二原创。 https://blog.csdn.net/qing_gee/article/details/72519313

select2是一款jquery插件,是普通form表单select组件的升级版。
可以定制搜索、远程数据集(Remote data,本篇主要介绍点)、无限滚动(数据分页功能,这一点很妙)、还有很多高端的参数设置(有需要的下次介绍)。
内置了40种国际化语言,不过这里我们只需要用到中文。
同时支持现代和传统浏览器内置,甚至包括惹人不高兴的IE8。

切记

如果后台返回的数据当中没有id,那么select2是无法选中的,所以切记!!!!!

那么,现在让我们开始一段select2的奇幻之旅吧!

一、惊艳的效果,来一睹为快吧

demo效果
demo检索结果
下拉刷新

本地实战结果
本地实战结果

二、导入css和js到网站上

1.使用CDN,节省自己网站的流量

<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js"></script> 

2.下载文件到本地,可以做一些个性的定制(比如说修改提示语)

git下载地址

<!-- select2 -->
<link rel="stylesheet" type="text/css" href="${ctx}/common/select2/css/select2.css" />
<script type="text/javascript" src="${ctx}/common/select2/js/select2.full.js"></script>
<!-- 中文国际化还需要进行参数设置 -->
<script type="text/javascript" src="${ctx}/common/select2/js/i18n/zh-CN.js"></script>

三、真刀真枪的干起来

第一步、定制页面个性化元素

<select name="parentid" class="js-data-example-ajax" href="${ctx}/member/loadMembersInfo.do?uid=${mem.uid}" style="width:400px" inputMessage="请输入会员编号(可部分匹配)">
    <option selected="selected" value="666">沉默王二</option>
</select>
  1. Java端通过name属性可获得select的value值。
  2. 设置class为js-data-example-ajax,页面加载时对该组件进行select2的初始化。
  3. href属性为ajax提供后台检索的URL。
  4. style设置组件的宽度。
  5. inputMessage属性定制个性化的提示语,默认的英文版为Please enter 1 or more characters,中文国际化为“请再输入至少1个字符”,都不太能满足个性化需求,所以需要改,后面介绍。
  6. 提供一个默认的option,页面没检索之前显示。

第二步、select2组件化,注释写得很详细了哦

<script type="text/javascript">
    $(function() {
        $("select.js-data-example-ajax").each(
        function() {
            var $this = $(this);
            $this.select2({
                language : "zh-CN",// 指定语言为中文,国际化才起效
                inputMessage : $this.attr("inputMessage"),// 添加默认参数
                ajax : {
                    url : $this.attr("href"),
                    dataType : 'json',
                    delay : 250,// 延迟显示
                    data : function(params) {
                        return {
                            username : params.term, // 搜索框内输入的内容,传递到Java后端的parameter为username
                            page : params.page,// 第几页,分页哦
                            rows : 10// 每页显示多少行
                        };
                    },
                    // 分页
                    processResults : function(data, params) {
                        params.page = params.page || 1;

                        return {
                            results : data.data,// 后台返回的数据集
                            pagination : {
                                more : params.page < data.total// 总页数为10,那么1-9页的时候都可以下拉刷新
                            }
                        };
                    },
                    cache : false
                },

                escapeMarkup : function(markup) {
                    return markup;
                }, // let our custom formatter work
                minimumInputLength : 1,// 最少输入一个字符才开始检索
                templateResult : function(repo) {// 显示的结果集格式,这里需要自己写css样式,可参照demo
                    // 正在检索
                    if (repo.loading)
                        return repo.text;

                    var markup = repo.username;

                    markup += repo.realname;

                    var markup = "<div class='select2-result-repository clearfix'>" + "<div class='select2-result-repository__avatar'><img src='"
                            + repo.headimgUrl + "' /></div>" + "<div class='select2-result-repository__meta'>"
                            + "<div class='select2-result-repository__title'>" + repo.username + "</div>";

                    if (repo.realname) {
                        markup += "<div class='select2-result-repository__description'>" + repo.realname + "</div>";
                    }

                    markup += "<div class='select2-result-repository__statistics'>"
                            + "<div class='select2-result-repository__forks'><i class='fa fa-user'></i> 下级会员数" + repo.children_count + " </div>"
                            + "</div>" + "</div></div>";

                    return markup;
                }, 
                templateSelection : function(repo) {
                    return repo.realname || repo.text;
                }// 列表中选择某一项后显示到文本框的内容
            });

        });
    });
</script>

第三步、Java端接收参数并返回结果集,不用我强调,这步很重要

@RequestMapping(value = "loadMembersInfo")
public void loadMembersInfo(HttpServletRequest request, HttpServletResponse response) throws IOException {
    Integer uid = StrUtil.parseStringToInt(request.getParameter("uid"));
    Members mem = this.memberService.selectByPrimaryKey(uid);


    // 分页参数的转换,需要和前台select2进行匹配,下文放代码
    BaseConditionVO vo = getBaseConditionVOForTable(request);
    vo.addParams("username", StrUtil.getUTF8String(request.getParameter("username")));
    vo.addParams("uid", uid);

    // 封装结果集,和前台select2也是匹配的。
    PageGrid page = createPageGrid(this.membersMapper.getPromoterList(vo, vo.createRowBounds()), vo,
            this.membersMapper.searchPromoterTotalCount(vo));

    // 以json格式写入到response
    out(page, response);

}

接下来,把关键的源码贴出来,可能和你的项目不吻合,但可以参考。

BaseConditionVO.java

public class BaseConditionVO {
    public final static int PAGE_SHOW_COUNT = 50;
    private int pageNum = 1;
    private int numPerPage = 0;
    private int totalCount = 0;
    private String orderField;
    private String orderDirection;

    /**
     * @Fields ps : 对参数类型进行封装.
     */
    private Map<String, Object> mo = new HashMap<String, Object>();

    public int getPageNum() {
        return pageNum;
    }

    public void setPageNum(int pageNum) {
        this.pageNum = pageNum;
    }

    public int getNumPerPage() {
        return numPerPage > 0 ? numPerPage : PAGE_SHOW_COUNT;
    }

    public void setNumPerPage(int numPerPage) {
        this.numPerPage = numPerPage;
    }

    public String getOrderField() {
        return orderField;
    }

    public void setOrderField(String orderField) {
        this.orderField = orderField;
    }

    public String getOrderDirection() {
        return "desc".equals(orderDirection) ? "desc" : "asc";
    }

    public void setOrderDirection(String orderDirection) {
        this.orderDirection = orderDirection;
    }

    public int getTotalCount() {
        return totalCount;
    }

    public void setTotalCount(int totalCount) {
        this.totalCount = totalCount;
    }

    public int getStartIndex() {
        int pageNum = this.getPageNum() > 0 ? this.getPageNum() - 1 : 0;
        return pageNum * this.getNumPerPage();
    }

    public RowBounds createRowBounds() {
        RowBounds ro = new RowBounds(this.getStartIndex(), this.getNumPerPage());
        return ro;
    }

    /**
     * @Title: addParams
     * @Description: 添加查询条件
     * @param key
     * @param value
     */
    public void addParams(String key, Object value) {
        this.getMo().put(key, value);
    }

    /** 
    * @Title: getParams 
    * @Description: 获取查询条件
    * @param key
    * @return
    */
    public Object getParams(String key) {
        return this.getMo().get(key);
    }

    /**
     * @return the mo
     */
    public Map<String, Object> getMo() {
        return mo;
    }

    /**
     * @param mo
     *            the mo to set
     */
    public void setMo(Map<String, Object> mo) {
        this.mo = mo;
    }
}

selec2的分页和Java端分页参数匹配

protected BaseConditionVO getBaseConditionVOForTable(HttpServletRequest req) {
    BaseConditionVO vo = new BaseConditionVO();
    // 当前页
    int currentPage = StrUtil.parseStringToInt(req.getParameter("page"));
    // 一页显示多少行
    int sizes = StrUtil.parseStringToInt(req.getParameter("rows"));
    // 排序
    String sortOrder = StrUtil.getString(req.getParameter("sord"));
    String sortCol = StrUtil.getString(req.getParameter("sidx"));
    vo.setNumPerPage(sizes);
    vo.setPageNum(currentPage);
    vo.setOrderField(sortCol);
    vo.setOrderDirection(sortOrder);

    return vo;
}

Java端到select2端的数据封装

@XStreamAlias("pageGrid")
@SuppressWarnings("rawtypes")
public class PageGrid {
    private int page;
    // 总页数,和select2的processResults.pagination匹配
    private int total;
    private int records;

    // 数据结果集,和select2的processResults.results匹配
    private List data;

    public int getPage() {
        return this.page;
    }

    public void setPage(int page) {
        this.page = page;
    }

    public int getTotal() {
        return this.total;
    }

    public void setTotal(int total) {
        this.total = total;
    }

    public int getRecords() {
        return this.records;
    }

    public void setRecords(int records) {
        this.records = records;
    }

    public List getData() {
        return this.data;
    }

    public void setData(List data) {
        this.data = data;
    }
}

mysql获取的数据源和PageGrid进行转换匹配

protected PageGrid createPageGrid(List list, BaseConditionVO vo, int searchTotalCount) {
    PageGrid pageGrid = new PageGrid();
    // 数据
    pageGrid.setData(list);
    // 当前页
    pageGrid.setPage(vo.getPageNum());
    // 总数目
    pageGrid.setRecords(list.size());

    // 总页数
    int total = 0;
    if (pageGrid.getRecords() != 0) {
        total = searchTotalCount % vo.getNumPerPage() == 0 ? searchTotalCount / vo.getNumPerPage()
                : searchTotalCount / vo.getNumPerPage() + 1;
    }

    pageGrid.setTotal(total);
    return pageGrid;
}

mybatis的分页,超简单,只要设置了createRowBounds,mybatis就会自动为你分页,这个就厉害了。

List getPromoterList(BaseConditionVO vo, RowBounds createRowBounds);

sql语句,这里的关键点是必须要回传id(m.uid as id)到select2.

<select id="getPromoterList" resultType="hashmap" parameterType="map">
    select
    m.uid as id,
    convert(m.username,char) username,
    m.realname,
    m.children_count,
    m.headimgUrl
    from
    members m
    where m.deleteflag=0
    <if test="mo.username != ''">and m.username like CONCAT('%', '${mo.username}', '%')</if>

    <choose>
        <when test="orderField !=null and orderField !=''">
            ORDER BY ${orderField}
            <if test="orderDirection != null and orderDirection != ''">${orderDirection}</if>
        </when>
        <otherwise>
            order by m.username DESC
        </otherwise>
    </choose>
</select>

你是不是没看见mysql的分页limit,嗯,这里无须关注,这就是框架要为我们做的事情。

总数

int searchPromoterTotalCount(BaseConditionVO vo);

count(0)就好

<select id="searchPromoterTotalCount" resultType="java.lang.Integer" parameterType="map">
    select count(0) as a
    from
    members m
    where m.deleteflag=0 
    <if test="mo.username != ''">and m.username like CONCAT('%', '${mo.username}', '%')</if>
</select>

out输出到response中

protected void out(Object result, HttpServletResponse response) throws IOException {
    ServletOutputStream out = response.getOutputStream();

    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.writeValue(out, result);
    out.flush();
}

到这,select2的remote功能在代码部分就完全贴出来完了。
不过,我最后还是要强调几个点:

1.分页的参数Java端和select2一定要对照起来。
2.回传的数据一定要传递一个id回来,否则回来的列表不能选中,为什么呢?调查select2的源码可以知道。

  Results.prototype.option = function (data) {
    var option = document.createElement('li');
    option.className = 'select2-results__option';

    var attrs = {
      'role': 'treeitem',
      'aria-selected': 'false'
    };

    if (data.disabled) {
      delete attrs['aria-selected'];
      attrs['aria-disabled'] = 'true';
    }

// id为空的情况下,删除的aria-selected,而aria-selected恰好又是列表选中的关键属性。
// 这个就是个坑,只能这么说,select2给出的api上完全不讲这点,我去!!!!!!!
    if (data.id == null) {
      delete attrs['aria-selected'];
    }
    ......
}

3.form表单如何获取select2的值?答案是,1.返回结果集必须有id,2.input标签上必须要name属性。
4.如何自定义inputMessage呢?

在select2.js中找到以下代码,注意注释部分

S2.define('select2/data/minimumInputLength',[

], function () {
  function MinimumInputLength (decorated, $e, options) {
    this.minimumInputLength = options.get('minimumInputLength');
    // inputMessage
    this.inputMessage = options.get('inputMessage');

    decorated.call(this, $e, options);
  }

  MinimumInputLength.prototype.query = function (decorated, params, callback) {
    params.term = params.term || '';

    if (params.term.length < this.minimumInputLength) {
      this.trigger('results:message', {
        message: 'inputTooShort',
        args: {
          minimum: this.minimumInputLength,
          input: params.term,
          inputMessage : this.inputMessage, // inputMessage,传递给i18n
          params: params
        }
      });

      return;
    }

    decorated.call(this, params, callback);
  };

  return MinimumInputLength;
});

select2.js中defaults中增加上inputMessage

    this.defaults = {
    ...
      minimumInputLength: 0,
      inputMessage: '',
      maximumInputLength: 0,
      ...
    };

然后在zh-CN.js文件中修改inputTooShort方法

inputTooShort : function(e) {
    if (e.inputMessage) {
        return e.inputMessage;// 增加inputMessage
    } else {
        var t = e.minimum - e.input.length, n = "请再输入至少" + t + "个字符";
        return n
    }

},

到这里嘛,真的该结束了。


欢迎关注我的公众号,给灵魂片刻安静!

微信扫一扫下方二维码即可关注 沉默王二 公众号:

相关实践学习
简单用户画像分析
本场景主要介绍基于海量日志数据进行简单用户画像分析为背景,如何通过使用DataWorks完成数据采集 、加工数据、配置数据质量监控和数据可视化展现等任务。
SaaS 模式云数据仓库必修课
本课程由阿里云开发者社区和阿里云大数据团队共同出品,是SaaS模式云原生数据仓库领导者MaxCompute核心课程。本课程由阿里云资深产品和技术专家们从概念到方法,从场景到实践,体系化的将阿里巴巴飞天大数据平台10多年的经过验证的方法与实践深入浅出的讲给开发者们。帮助大数据开发者快速了解并掌握SaaS模式的云原生的数据仓库,助力开发者学习了解先进的技术栈,并能在实际业务中敏捷的进行大数据分析,赋能企业业务。 通过本课程可以了解SaaS模式云原生数据仓库领导者MaxCompute核心功能及典型适用场景,可应用MaxCompute实现数仓搭建,快速进行大数据分析。适合大数据工程师、大数据分析师 大量数据需要处理、存储和管理,需要搭建数据仓库?学它! 没有足够人员和经验来运维大数据平台,不想自建IDC买机器,需要免运维的大数据平台?会SQL就等于会大数据?学它! 想知道大数据用得对不对,想用更少的钱得到持续演进的数仓能力?获得极致弹性的计算资源和更好的性能,以及持续保护数据安全的生产环境?学它! 想要获得灵活的分析能力,快速洞察数据规律特征?想要兼得数据湖的灵活性与数据仓库的成长性?学它! 出品人:阿里云大数据产品及研发团队专家 产品 MaxCompute 官网 https://www.aliyun.com/product/odps&nbsp;
相关文章
|
2月前
|
存储 分布式计算 Hadoop
maxcompute配置问题之加速查询超时配置回退如何解决
MaxCompute配置是指在使用阿里云MaxCompute服务时对项目设置、计算资源、存储空间等进行的各项调整;本合集将提供MaxCompute配置的指南和建议,帮助用户根据数据处理需求优化其MaxCompute环境。
31 1
|
1月前
|
Java 关系型数据库 数据库连接
MyBatis Plus 解决大数据量查询慢问题
MyBatis Plus 解决大数据量查询慢问题
|
3月前
|
前端开发 JavaScript
js + ajax实现商品列表页到详情页的跳转
js + ajax实现商品列表页到详情页的跳转
|
1月前
|
存储 SQL 关系型数据库
【MySQL 数据库】6、一篇文章学习【索引知识】,提高大数据量的查询效率【文末送书】
【MySQL 数据库】6、一篇文章学习【索引知识】,提高大数据量的查询效率【文末送书】
56 0
|
2月前
|
DataWorks API 数据安全/隐私保护
dataworks数据集问题之获取表列表失败如何解决
DataWorks数据集是指在阿里云DataWorks平台内创建、管理的数据集合;本合集将介绍DataWorks数据集的创建和使用方法,以及常见的配置问题和解决方法。
46 6
|
3月前
|
SQL 存储 大数据
【拿走不谢】大数据高效查询神器--bitmap
【拿走不谢】大数据高效查询神器--bitmap
|
4月前
|
SQL 分布式计算 MaxCompute
这些警告信息是MaxCompute在执行SQL查询时生成的
这些警告信息是MaxCompute在执行SQL查询时生成的
34 3
|
5月前
|
SQL 分布式计算 调度
在MaxCompute中,你可以通过SQL语句来查询和导出实例的运行状态和时间等信息
在MaxCompute中,你可以通过SQL语句来查询和导出实例的运行状态和时间等信息
50 5
|
JSON JavaScript 前端开发
jQuery+Ajax+PHP无刷新分页
下载演示地址:http://www.erdangjiade.com/js/2.html 效果图: 本文使用jQuery+Ajax+PHP+Mysql,通过实例讲解如何实现Ajax无刷新分页效果。
1173 0
|
5月前
|
XML 前端开发 JavaScript
什么是Ajax和jquery
什么是Ajax和jquery
38 0