select2,利用ajax高效查詢大資料列表(可搜尋、可分頁)
select2是一款jquery外掛,是普通form表單select元件的升級版。
可以定製搜尋、遠端資料集(Remote data,本篇主要介紹點)、無限滾動(資料分頁功能,這一點很妙)、還有很多高階的引數設定(有需要的下次介紹)。
內建了40種國際化語言,不過這裡我們只需要用到中文。
同時支援現代和傳統瀏覽器內建,甚至包括惹人不高興的IE8。
切記:
如果後臺返回的資料當中沒有id,那麼select2是無法選中的,所以切記!!!!!
那麼,現在讓我們開始一段select2的奇幻之旅吧!
一、驚豔的效果,來一睹為快吧
本地實戰結果
二、匯入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.下載檔案到本地,可以做一些個性的定製(比如說修改提示語)
<!-- 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>
- Java端通過name屬性可獲得select的value值。
- 設定class為js-data-example-ajax,頁面載入時對該元件進行select2的初始化。
- href屬性為ajax提供後臺檢索的URL。
- style設定元件的寬度。
- inputMessage屬性定製個性化的提示語,預設的英文版為Please enter 1 or more characters,中文國際化為“請再輸入至少1個字元”,都不太能滿足個性化需求,所以需要改,後面介紹。
- 提供一個預設的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
}
},
到這裡嘛,真的該結束了。
歡迎關注我的公眾號,給靈魂片刻安靜!
微信掃一掃下方二維碼即可關注 沉默王二 公眾號: