【Bootstrap】 bootstrap-table表格组件
【Bootstrap-table】
顾名思义,这个组件专注于bootstrap风格的表格的设计,并且提供了很多表格的基础和进阶的功能,给我们开发前端的表格省下很多力气。
本文主要参考这位博主的系列文章:
【http://www.cnblogs.com/landeanfen/p/5005367.html】
有兴趣的可以移步看原版,我自己做一个笔记性质的东西。
■ 基本使用
和大多数bs组件一样,首先我们要确保引入以下几个基本文件:
<link href="/static/css/bootstrap.min.css" rel="stylesheet" /> <script src="/static/js/jquery.min.js"></script> <script src="/static/js/bootstrap.min.js"></script>
然后是引入bootstrap-table的一些文件:
<link href="/static/bootstrap-table/dist/bootstrap-table.min.css" rel="stylesheet" /> <script src="/static/bootstrap-table/dist/bootstrap-table.min.js"></script> <script src="/static/bootstrap-table/dist/locale/bootstrap-table-zh-CN.min.js"></script>
最后一个是国际化显示文件,即支持组件显示中文。
引入文件完毕之后,我们在HTML中简单地写上一个table标签即可:
<table id="bsTableTest"></table>
然后在相应的js中写如下内容:
$(document).ready(function(){ $('#skillContentTable').bootstrapTable({ url: querySkillUrl, columns: [...], resizable: true, pagination: true, sidePagination: 'client', pageNumber: 1, pageSize: 10, pageList: [10,20,50,'All'], search: true, showRefresh: true, showToggle: true, showColumns: true, }); });
依次解释一下各个参数的话:
url 代表了表格加载数据(包括页面的首次加载和点击刷新按钮等时机)时向后端某一个url索取数据。此参数和data参数可二选其一
columns 指出了表格的<th>部分的结构,比较重要且似乎必须在前端写出,这里因为比较长没有具体写出来,下面再细说。
resizable 可以让用户通过拖动调整各个列的列宽,需要其他组件的支持,上面也没有写出来,下面细说
pagination 是否分页。bootstrap-table可以自动给我们生成分页,这个功能很好(回想起自己手动写分页的痛苦。。)
sidePagination bs-table中分页分成两种模式,即所谓的server模式和client模式。两种模式下后端给出的数据结构不同,分页运算也在不同的地方完成。至于结构如何不同后面细说
pageNumber,pageSize,pageList 很显然是一些分页的参数,pageNumber是默认显示第几页,一般设置成1;pageSize是默认一页显示多少行;pageList是提供给用户可以调整的一页显示的行数,是个列表
search 是否显示搜索框,bs-table自带一个搜索框,如果有需要可以直接在搜索框中进行模糊搜索
showRefresh 是否显示刷新按钮,在右上角
showColumns 是否显示“显示列控制"按钮,通过这个按钮可以调整页面上显示哪些字段而不显示哪些
showToggle 是否显示试图切换按钮,通过这个按钮可以在表格切换和卡片视图间切换
除了上面这些还有一些基本的初始化参数:
striped 设置为true时表格样式为table-striped的样子
classes 渲染出的table的class,默认是table table-hover
strictSearch 启用完全匹配搜索而不是模糊搜索
undefinedText 当数据为undefined时显示的字符,默认是'-'。
uniqueId 指定一列作为ID,但是没有直接作用,在相关方法中才有用
height 指出表格高度,当表格高度超过指定值时就有滚动条了。
maintainSelected 如果设置为true,那么表格在多页且有checkbox之类的东西在时,翻页后仍然记住之前选中项
■ 上述参数的细化说明
先来说说resizable吧,这个参数是设置为true之后还需要在页面上引入下面两个东西:
<script src="colResizable-1.6.min.js"></script> <script src="bootstrap-table/dist/extensions/resizable/bootstrap-table-resizable.min.js"></script>
请注意,colResizable-1.6.min.js不是bootstrap-table的一部分,需要另外去网上找并且下载。这是一个jquery调整列宽的组件。而bootstrap-table-resizable.min.js是bootstrap-table中的一个扩展。在extensions目录下。这个目录中有很多好用的拓展,以后可能也会再说到。
再简单说下data,data参数可以在js内部就给表格充实内容。但是由于很少会有在前端计算决定表格内容的场景,基本上都是用不到的,除了在测试或者干嘛的时候用一下。
● columns参数说明
正如上面说明的,columns参数的值指出了这个表格<th>部分应该如何构建,其数据结构是这样的:比如我要构建一张口袋妖怪的招式的统计表格:
columns: [{ checkbox:true, align: 'center' },{ field: 'id', title: '编号', sortable: true },{ field: 'cn_name', title: '中文名', sortable: true },{ field: 'jp_name', title: '日文名' },{ field: 'en_name', title: '英文名' },{ field: 'nature', title: '属性', sortable: true },{ field: 'generation', title: '世代', sortable: true },{ field: 'power', title: '威力' },{ field: 'hirate', title: '命中' },{ field: 'type', title: '攻击类型' },{ field: 'pp', title: 'pp点数' }],
可以看出其值是一个字典的列表(or you say object的数组),field指出了字段名而title指出了页面上显示的列名。此外还有额外的参数sortable指出了用户可以点击某列表头,依此列对当前数据进行排序。另外还可以注意第一项的checkbox:true,这表示在开头的第一列有一列checkbox,点击每一行前面的可以选中这一行,而点击表头的checkbox更是可以全选。
除了上述列参数,还有很多其他的列参数,比如:
radio 和checkbox类似,渲染出一列radio
align,vlign,halign 指出了列的对其/垂直对其/水平对齐方式。
visible 指出该列是否默认显示,如果设置为false即隐藏了,那么可以通过showColumns的那个按钮再调出来
formatter 是一个函数,可以对传入此列所有单元格的数据做二次加工。这个函数接收三个参数value , row , index。这三个参数分别代表了单元格的值,单元格所在的行的行对象,单元格下标。除此之外formatter还有一个很重要的用法,那就是向单元格中添加按钮等非文本而是HTML的内容。
responseHandler 也是一个函数,当后端传递来的数据不符合bootstrap-table需要的格式时可以通过这个函数做一个适应性处理,接受一个reponse作为参数代表收到的回应的JSON。
这里也只是简单的说了一些列参数,更完全的参数列表可以参考官方文档【http://bootstrap-table.wenzhixin.net.cn/zh-cn/documentation/#%E5%88%97%E5%8F%82%E6%95%B0】
● sidePagination和后端传来的数据格式
当设置sidePagination为client模式时,即所有分页排序操作都在客户端的浏览器中完成。当数据总量比较少时这种模式可以减少前后端多余的通信成本,比较适合。而且在这种模式下,后端传来数据格式比较简单。比如上面这个表,在后端的django框架中我可以这样返回:
def query_for_skill(request): data_set = [] for skill in Skill.objects.all(): data_set.append(skill.getAttrs()) # return HttpResponse(json.dumps({'rows':data_set,'total':len(data_set)}),content_type='application/json') return HttpResponse(json.dumps(data_set),content_type="application/json")
这里模型类Skill还实现了一个方法getAttrs,这个方法是返回了模型类对象定义的各个字段和各自值对应的字典。所以当我们要把数据一股脑儿地传递给前端时,Skill.objects.all()是一个模型类对象的列表,前端尚无法识别,要我们做个小循环来处理下数据,这样就传递给前端的数据就是JSON的,像这样的: [{id: 1,cn_name: 'xxx',power:xx...}, ...]。因为采取客户端分页排序的方式,所以一股脑儿把数据传递给前端之后,前端再进行分页排序等操作时不会再向后端请求数据。效果:
同样的分页效果,还可以通过server的sidePagination来实现。
当通过server的sidePagination来做时需要注意后端返回的数据结构有变化,应该像上面view函数中被注释掉的那个return的json串那样。即返回一个字典,含有rows和total两个key,前者的value和之前client模式的返回内容一样,而后者的value是指出了所有记录的总长度。
当采用server模式时,像上面那样的view函数就不是很管用了。事实上,点击server模式的分页,会向后台发出一个新的ajax请求来请求数据,其请求参数默认包含了limit,offset,search,sort,order,分别代表一页显示数目;跳过前offset条记录;查找字符串内容;排序字段名;排序方式('asc'或'desc')。如果想要传入更多的参数可以设置queryParams这个初始化参数。这个ajax请求默认是GET方法,如果想要用POST,可以在初始化参数method中指出
queryParams的值是一个函数对象。这个函数接受一个参数params,代表上面提到的那五个参数和各自值形成的对象(如果是空值则是undefined)。最终返回是一个经过函数处理的object作为真的请求参数发出请求。如果返回false则取消请求,所以也可以在这个函数中增加自定义过滤条件或进行查询条件合法性的检查。
对view函数的改造,很明显应该适应于前端传递过来的参数。在后端做出一些过滤筛选后返回一个有限的结果集。比如像上面那个函数我们可以这么改造来适应分页、排序的需求:
def query_for_skill(request): data_set = [] offset = request.GET.get('offset') limit = request.GET.get('limit') sort = request.GET.get('sort') order = request.GET.get('order') length = len(Skill.objects.all()) if not offset or not limit: targets = Skill.objects.all() if sort: sort = sort if order == 'asc' else '-'+sort targets = targets.order_by(sort) else: offset = int(offset) limit = int(limit) if sort: sort = sort if order == 'asc' else '-' + sort targets = Skill.objects.all().order_by(sort)[offset:offset+limit] else: targets = Skill.objects.all()[offset:offset+limit] for skill in targets: data_set.append(skill.getAttrs()) return HttpResponse(json.dumps({'rows':data_set,'total':length}),content_type='application/json')
再额外提一句,在client模式中,search是直接在客户端中完成搜索,不涉及额外通信以及耗费后台资源。但是search该参数并没有指出是search了哪个字段的内容,这就导致,如果在server模式下也用search的话,势必要对整个表的所有字段进行检查,来寻找和search给出的值相关的内容。这个不仅编程麻烦,还会给数据库带来很大负担。所以不推荐用bootstrap-table自带的search。如果有search的这需求,可以考虑通过在表格上面写一个filter,然后在queryParams的函数中加上额外的参数来进行查询。
■ 相关方法
和其他很多组件一样,bs-table有自己的一些方法接口,可以让程序员实现自己的逻辑。这些方法的调用方法主要是$('#bsTableTest').bootstrap('<方法名>',<一个object形式的参数>)。常用的方法有:
getOptions 获取表格的一些基本属性,返回一个object,key有像conlumns,data,sortOrder,class这些
getSelections 获取被选中的(包括radio或者checkbox)各行对象组成的列表。
load,append,prepend 这三个方法的参数都是data,即[{...},{...}...]形式的数据,load是清空当前表中所有数据,append是在表尾加,prepend是在第一行前面插入数据
remove 参数是一个object,含有两个字段,field和values。通过两个字段的值可以定位一个或多个单元格,把这些单元格所在的行删除。例如bootstrapTable('remove',{field:'id',values:[3]})。需要注意的是即便是只定位一个单元格,即values后列表中只有一个值,那也要写出是个列表,否则会识别失败
insertRow,updateRow 这两个方法分别用于在指定位置插入一个新行或者更新指定行的信息。参数有index和row,如bootstrapTable('insertRow',{index:2,row:{id:99,name:'New Row',desc:'Test'}}),需注意,如果某些字段没有在params中给出值的话,那么insert时默认这些字段为undefined,update时默认这些字段没有改变。
getRowByUniqueId 这个方法要配合初始化时的uniqueId参数指定的字段,参数为此字段的某个值,返回值等于参数中给出值的那一行的行对象
showRow,hideRow 显示隐藏行,参数可用index,也可用uniqueId,如bootstrapTable('showRow',{index:2})
showLoading,hideLoading 显示/隐藏加载状态
mergeCells 用于合并单元格,参数有四个,如果把合并后最左上角的那个单元格称为初始单元格的话,那么index是初始单元格的行的index,field是初始单元格所在列字段名。这两个参数确定了一个起始单元格,然后还有两个参数rowspan和colspan都是int型,指出了从初始单元格开始向下并几行向右并几列。
updateCell index,field和value三个参数用来更新单个单元格内的值
refresh 刷新表格数据,可以加入参数url指定请求发向的url(可以是一个新的),silent:true时静默更新,query:{} 可以指出一些新的ajax请求时的参数。
resetSearch 可以设置搜索框中文字,这个方法比较特殊的是参数不是一个object而是单纯的string。例如bootstrapTable('resetSearch','Hello')
resetView/Width 可以重设样式(如宽度高度等),常用在页面大小、宽度等发生变化时。比如侧边栏收放引起表格所在容器宽度发生变化,容易导致表头和表内容列错位。在宽度变化时调用这个函数便可以自动修正。
checkAll,uncheckAll 全选/全不选当前页面中的行
check/uncheck 选中/不选一行,同resetSearch一样,不用传递object,直接写int型的index数据即可例如 bootstrapTable('check',2)
checkBy/uncheckBy bootstrapTable('checkBy',{field:'id',values:[1,3,5]}),选中部分,条件由参数给出
getHiddenRows/getHiddenColumns 获取隐藏着的行和列们
getScrollPosition/scrollTo 当表格有滚动条时,前者返回滚动条滚动位置,后者设置。单位是px,scrollTo可以是'bottom'来拉到底
filterBy 在客户端进行数据过滤,参数是field和values
prevPage/nextPage/selectPage 跳往前一页/后一页/指定页
■ 相关事件
除了方法之外,组件另外一个重要的组成部分就是事件了。boostrap-table的事件的使用方法并不是经典的$(xxx).on('事件',function),而是采用将事件作为初始化参数的一部分传入初始化函数中。比如:
$('#bsTableTest').bootstrapTable({ xxx: xxx, 事件: function(params){...} });
正如上面所示,事件会关联一个函数,而不同的事件,关联的函数有哪些参数也都是不一样的。下面列出主要可能用到的事件:
onClickRow 点击一行时触发的事件,参数有row , element , field三个,分别代表点击行的行对象,点击行的jquery<tr>对象,点击的列的title
onDblClickRow 双击行事件,参数和onDblClickRow一样
onClickCell 点击单元格触发的事件,参数包括field , value , row , element,分别表示点击单元格所在列title,单元格的值,单元格所在行行对象,单元格的jquery<td>对象
onDblClickCell 双击单元格事件,参数和onClickCell一样
onSort 点击排序按钮进行数据排序时触发的事件,包括参数name和order分别代表排序字段名和排序方式(asc和desc)
onCheck/onUncheck 当某一行被选中/取消选中时触发事件,参数有row和element
onCheckAll/onUncheckAll 参数是rows,一个被选中所有行对象的数组。需要注意,手动一个一个选择时不会触发这个事件,只有按表头的那个全选,全选行时才会触发。
onLoadSuccess/onLoadError 当加载数据成功/失败时触发的事件,前者参数data,后者参数status(HTTP返回码,如500,404之类的)
onColumnSwitch 当设置某一列可见/不可见时触发,参数是field和checked,field是被操作的字段title名,而checked是boolean值,表示操作后该字段是否是显示状态。
onPageChange 换页时触发事件,参数有number和size,分别代表跳转后位于第几页且页面内有多少条记录
onSearch 使用bs-table自带搜索框进行搜索时触发事件,参数是text即搜索关键文
onRefresh 刷新表格数据是触发的事件,参数params是一个挺复杂的对象,包含了此次刷新的一些信息。不具体展开说了,有需要的话可以测试下
■ 一些实践
● 关于toolbar
不知道前面有没有说到toolbar相关内容。一个经典的bootstrap-table往往在左上角(右上角是showColumns,showToggle控制的那些按钮)有一个toolbar,比如可以把新建,编辑,删除等按键放在这个位置。而在初始化时加入初始化参数toolbar,其值是一个jquery的selector,此时bootstrap-table就会自适应地把这个toolbar区域放到合适的地方。
不过需要注意的是,toolbar参数只是样式和位置上的调整,并不会自动给予toolbar中任何内容js逻辑,所以和右上角那些按钮不同,toolbar中的按钮的逻辑需要自己开发js来实现。
一个带有toolbar的HTML界面可能是这样的:
<div id="toolbar" class="btn-group"> <button id="btn_add" type="button" class="btn btn-default" title="新增"> <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> </button> <button id="btn_edit" type="button" class="btn btn-default" title="编辑"> <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> </button> <button id="btn_delete" type="button" class="btn btn-default" title="删除"> <span class="glyphicon glyphicon-remove" aria-hidden="true"></span> </button> </div> <table id="skillContentTable"> </table>
然后在初始化时加入toolbar参数:
$('#skillContentTable').bootstrapTable({ /* 其他参数... */ toolbar: '#toolbar' });
就可以得到左上角一个toobar了。toolbar的具体逻辑在js中自己实现。
● 横向滚动的表格
最开始的问题是随着字段的变多,每个单元格中内容变长,会发生内容在单元格中自动换行,把行高撑大,然后这个表格就非常不好看了。解决的办法是两个手段,1.让表格可以横向滚动从而保证表格长度即使在字段很多的时候也不会爆炸;2. 禁用单元格中的自动换行,当单元格长度不够用时不要换行而是强行增宽表格。
(说起来其实这两个和这个bs-table组件也没啥关系。。)1的实现方法是在table的外面包裹一层div.table-responsive,这个样式的div包裹住的table,当屏幕变小变窄的时候自动会出现横向滚动条从而不会爆炸。2的实现方法是在th和td中添加样式white-space: nowrap;,结合这两者可以得到一个单元格内容永远是单行,表格长度不够用了就出现横滚条也不会撑大整个页面的宽度。