通过angularjs的directive以及service来实现的列表页加载排序分页
前两篇:(列表页的动态条件搜索,我是如何做列表页的)分别介绍了我们是如何做后端业务系统数据展示类的列表页以及动态搜索的,那么还剩下最重要的一项:数据展示。数据展示一般包含三部分:
- 数据列头
- 数据行
- 分页统计信息,分页导航
技术依赖项:基于angularjs的MVVM模式,后台是spring mvc。
数据表格需求:
- 需要支持列头的排序
- 需要支持单页操作,局部更新(angular model更新),比如更新某行数据成功后,自动更新当前行的数据,而不需要刷新页面或者另外请求后台数据。
- 需要支持数据逻辑运算以及复杂的html格式的数据,比如根据不同的数据值展示不同的按钮,文本等,展示的文本需要经过特定的逻辑运算等。
- 需要有统计信息以及分页展示
第三方的组件:
- jquery.datatable http://www.datatables.net/
最终展示的结果是html table,优点是支持嵌套表格等复杂功能,缺点是加载的文件大,且不能满足上面的需求angular model更新,对于复杂数据展示控制起来也比较复杂,需要额外的js编码工作。
- angular ui-grid https://github.com/angular-ui/ui-grid/wiki/Getting-started
最终展示的结果是div,与angularjs兼容性很好,能支持表格在线编辑,缺点同样也是不能满足上面的需求angular model更新,对于复杂数据展示控制起来也比较复杂,需要额外的js编码工作。
第三方组件的特点就是功能多,但有些看起来很高级的功能我们基本上都不用,比如在线表格数据的编辑,嵌套表格等。我们必须的功能只有这些:排序,数据展示,分页,如果能支持angular model更新更好。所以我决定结合html table,angularjs来完成上面的需求。
数据加载:
由于我们的数据动态查询方案的存在,决定了大部分页面前后台交互的模式是相同的,所以采用angular service来提供一个listService供列表页使用。主要是分页大小的选择框,以及定义了一些可配置的参数,比如执行查询的请求地址,由于我们的动态查询以表单提交,所以是将整个表单参数序列化之后再加上分页相关信息然后提交到后端查询。
angular.module('app.service', ['app.constant'])
.service('$listService', function () {
var $scopeLocal = {};
var pageSizeList = [{
"text": "10",
"value": "10"
}, {
"text": "20",
"value": "20"
}];
var defaultOptions = {
beforeSend: function () {
},
callback: function ($scope, data) {
},
error: function () {
},
pageSize: pageSizeList[0].value,
searchFormId: "searchForm",
};
this.init = function ($scope, option) {
var options = $.extend({}, defaultOptions, option);
$scopeLocal = $scope;
$scopeLocal.pageSizeList = pageSizeList;
$scopeLocal.pageRequest = {
"pageNum": 1, "pageSize": options.pageSize
}; $scopeLocal.pageRequest.getResponse = function (orderBy) {
var requestData = $("#" + options.searchFormId + "").serialize();
var url = options.listUrl + "?" + requestData + "&pageNum=" + $scopeLocal.pageRequest.pageNum + "&pageSize=" + $scopeLocal.pageRequest.pageSize;
if(angular.isDefined($scopeLocal.pageRequest.orderBy)&&$scopeLocal.pageRequest.orderBy!=""){
url+="&orderBy="+$scopeLocal.pageRequest.orderBy;
}
$.ajax({
type: "POST",
url: url,
dataType: 'json',
async: false,
beforeSend: options.beforeSend,
error: options.error,
success: function (data) {
$scopeLocal.pageResponse = data;
$scopeLocal.content = data.list;
options.callback($scopeLocal, data);
}
});
}; this.get = function () {
$scopeLocal.pageRequest.getResponse();
};
};
})
使用时,只需要注入listService,然后配置上参数即可:
mainApp.controller('manageCtrl', function ($scope, $http, $listService) {
var options = {
listUrl:"<c:url value="/theme/getAllByPage"/>"
};
$listService.init($scope, options);
$listService.get();
});
数据排序:
列头排序的方案是在列头上增加排序字段,通过angular directive来实现。排序的图片以及变换的样式是从jquery.datatable借鉴过来,逻辑并不复杂,无非就是显示排序标签以及根据用户的点击变换排序图标。
angular.module('app.directives', []).directive("sortName", [ function() {
return {
restict : "A",
link : function(scope, element, attrs) {
var sortName = attrs["sortName"];
var sortType = attrs["sortType"];
if (!angular.isString(sortName) || sortName == "")
return;
if (!angular.isString(sortType) || sortType == "") {
element.removeClass("sorting").removeClass("sorting_asc").removeClass("sorting_desc").addClass("sorting").attr("sort-type","asc");
}
$(element).bind("click", function(){
var thisObj=$(this);
var sortType = thisObj.attr("sort-type");
if (!angular.isString(sortName) || sortName == "")
return;
if (!angular.isString(sortType) || sortType == "")
return;
var orderBy = sortName + " " + sortType;
scope.pageRequest.orderBy=orderBy; scope.pageRequest.getResponse();
if (sortType == "asc") {
thisObj.removeClass("sorting").removeClass("sorting_asc").removeClass("sorting_desc").addClass("sorting_asc").attr("sort-type","desc");
} else if (sortType == "desc") {
thisObj.removeClass("sorting").removeClass("sorting_asc").removeClass("sorting_desc").addClass("sorting_desc").attr("sort-type","asc");
}
thisObj.siblings().each(function (){
var item=$(this);
if(typeof(item.attr("sort-name"))!="undefined"){
item.removeClass("sorting").removeClass("sorting_asc").removeClass("sorting_desc").addClass("sorting");
} });
scope.$apply();
});
}
}
} ]);
一个scope作用域的问题,在directive中获得的scope比较特殊,它的值变更不会影响外层页面上的$scope。因为我们需要在用户点击排序按钮后进行数据更新,所以我们需要调用$apply方法将scope的变化传播出去。
html中只需要在列头指定排序字段即可实现排序功能:sort-name,值是需要排序的字段:
<th class="col-md-1" sort-name ="id">编号</th>
<th class="col-md-4" sort-name ="name">名称</th>
数据展示:
直接在页面中采用table来布局,数据行采用angularjs来做加载。table布局的优点在于:直观上很清晰,处理某些特殊数据行时也比较容易,重要的是能够很容易的支持angular model 更新。
<table id="datatableTheme" cellpadding="0" cellspacing="0" border="0"
class="datatable table table-striped table-bordered table-hover">
<thead>
<tr>
<th>编号</th>
<th>名称</th>
<th>状态</th>
<th>描述</th> <th>操作</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in content">
<td ng-bind="item.id"></td>
<td ng-bind="item.name"></td>
<td>
<div ng-show="item.status=='1'">
<span class="label label-success">启用</span>
</div>
<div ng-show="item.status =='0'">
<span class="label label-danger">禁用</span>
</div>
</td>
<td ng-bind="item.description"></td>
<td>
<a href="javascript:void(0)" data-toggle="modal" ng-click="edit(item.id)"
data-original-title="编辑"> <span
class="label label-primary">编辑</span>
</a>
<a href="javascript:void(0)" ng-show="item.status=='0'" ng-click="enabled(item)"> <span
class="label label-primary">启用</span>
</a>
<a href="javascript:void(0)" ng-show="item.status=='1'" ng-click="enabled(item)"> <span
class="label label-primary">禁用</span>
</a>
</td> </tr> </tbody>
</table>
分页信息:
采用angularjs ui自带的uib-pagination。由于需要支持当前页记录大小的选择,如果每个页面都需要包含分页相关内容,这样代码会比较冗余,于时很容易的我们可以借助angular directive来解决:
angular.module('app.directives', []).directive("pagerFooter", [ function() {
return {
restrict : "A",
link : function(scope, element) {
return null;
},
templateUrl : "../app/template/pagerFooter.html"
}
} ])
<meta charset="UTF-8"> <div class="row form-inline">
<div class="col-md-6">
<span> 每页
<ui-select ng-model="pageRequest.pageSize" ng-change="pageRequest.getResponse()" theme='select2' style="min-width:35px;" >
<ui-select-match>{{$select.selected.text}}</ui-select-match>
<ui-select-choices repeat="item.value as item in (pageSizeList | filter: $select.search)">
<div ng-bind="item.text"></div>
</ui-select-choices>
</ui-select>
条记录 总共<span ng-bind="pageResponse.total"></span>条记录
</span>
</div>
<div class="col-md-6 text-right">
<uib-pagination
total-items="pageResponse.total"
ng-model="pageRequest.pageNum"
max-size="4"
class="pagination-sm"
boundary-links="true"
force-ellipses="false"
first-text="首页"
last-text="末页"
previous-text="上一页"
next-text="下一页"
num-pages="pageResponse.pages"
ng-change="pageRequest.getResponse()"
items-per-page="pageRequest.pageSize"
>
</uib-pagination>
</div>
</div>
使用时,我们只需要这样指定:加一个pager-footer的属性。
<div class="box-body" pager-footer>
</div>
写这个指令时遇到一个编码问题,模板页中出现的中文,在spring mvc环境下调用中乱码,最终在web.xml中增加配置得以解决:
<mime-mapping>
<extension>html</extension>
<mime-type>text/html;charset=utf-8</mime-type>
</mime-mapping>
目前还有一个疑问没有得到解决,就是模板页中必须还要指定<meta charset="UTF-8">,否则也会显示成乱码,回头找时间整体研究下spring mvc下的编码。
数据行数据的model更新
以避免通过二次请求或者刷新页面来重新加载数据。比如行数据中有状态一栏,操作列会根据状态值动态显示启用或者停用按钮,当用户点击启用按钮操作成功后,当前数据行的状态栏数据需要动态更新,且不需要请求后台也不需要刷新页面,我们可以非常容易的通用ng-bind来让其自动更新:
操作列绑定事件:
<td>
<a href="javascript:void(0)" data-toggle="modal" ng-click="edit(item.id)"
data-original-title="编辑"> <span
class="label label-primary">编辑</span>
</a>
<a href="javascript:void(0)" ng-show="item.status=='0'" ng-click="enabled(item)"> <span
class="label label-primary">启用</span>
</a>
<a href="javascript:void(0)" ng-show="item.status=='1'" ng-click="enabled(item)"> <span
class="label label-primary">禁用</span>
</a>
</td>
操作成功后更新model,页面数据自动更新。
$scope.enabled=function(theme)
{
bootbox.confirm("确认操作吗?", function (flag) {
if (flag) {
var status=theme.status==1?0:1;
var model={id:theme.id,status:status};
$http.post("<c:url value="/theme/enabled"/>",model).success(function(ret){
if (ret.err) {
bootbox.alert(ret.err);
}
else {
theme.status=status;
bootbox.alert("操作成功!"); }
});
}
});
};
列表页最终效果
上述的功能虽然不能解决所有场景的问题(嵌套表格,在线编辑表格,换肤等),但常见的业务操作均能满足,足够简单,不需要依赖第三方组件,重要的是能够完成其它js组件所不擅长的model更新场景以及复杂列的运算以及控制。我目前还在寻找其它的组件,如果有即能满足上述的需求又使用简单那么也是可以替换的,但做为学习总结总结倒也不错。
通过angularjs的directive以及service来实现的列表页加载排序分页的更多相关文章
- 通过angularjs的directive以及service来实现的列表页加载排序分页(转)
前两篇:(列表页的动态条件搜索,我是如何做列表页的)分别介绍了我们是如何做后端业务系统数据展示类的列表页以及动态搜索的,那么还剩下最重要的一项:数据展示.数据展示一般包含三部分: 数据列头 数据行 分 ...
- AngularJS进阶(三十六)AngularJS项目开发技巧之利用Service&Promise&Resolve解决图片预加载问题(后记)
AngularJS项目开发技巧之利用Service&Promise&Resolve解决图片预加载问题(后记) 前言 在"AngularJS项目开发技巧之图片预加载" ...
- Windows Service 项目中 Entity Framework 无法加载的问题
Windows Service 项目引用了别的类库项目,别的项目用到了 Entity Framework(通过Nuget引入),但是我的 Windows Service 无法开启,于是我修改了 App ...
- [AngularJS] 使用AngularAMD动态加载Service
[AngularJS] 使用AngularAMD动态加载Service 前言 「使用AngularAMD动态加载Controller」:这篇文章里介绍如何使用AngularAMD来动态加载Contro ...
- angularJS+requireJS实现controller及directive的按需加载
最近因为项目的比较大,需要加载的js文件较多,为了提高首屏页面的加载速度,需要对js文件进行按需加载,然后网上参考了一些资料,自己也深入研究一番之后,实现了按需加载控制器js文件及指令js文件的效果: ...
- AngularJS + ui-router + RequireJS异步加载注册controller/directive/filter/service
一般情况下我们会将项目所用到的controller/directive/filter/sercive预先加载完再初始化AngularJS模块,但是当项目比较复杂的情况下,应该是打开对应的界面才加载对应 ...
- AngularJS中Directive指令系列 - 基本用法
参考: https://docs.angularjs.org/api/ng/service/$compile http://www.zouyesheng.com/angular.html Direct ...
- AngularJs学习笔记--Managing Service Dependencies
原版地址:http://docs.angularjs.org/guide/dev_guide.services.managing_dependencies angular允许service将其他ser ...
- AngularJS之directive
AngularJS之directive AngularJS是什么就不多舌了,这里简单介绍下directive.内容基本上是读书笔记,所以如果你看过<AngularJS up and runnin ...
随机推荐
- KnockoutJS 3.X API 第四章(13) template绑定
目的 template绑定(模板绑定)使用渲染模板的结果填充关联的DOM元素. 模板是一种简单方便的方式来构建复杂的UI结构 . 下面介绍两种使用模板绑定的方法: 本地模板是支持foreach,if, ...
- 在android中用跑马灯的效果显示textview
大家好,在我们通常的android project中,通常需要用到textview这一个布局文件,并且对于这一个显示布局所需要的文本文字内容. 下面我们就来介绍一种方法来实现在android中用跑马灯 ...
- Android源码
Android 源码:http://grepcode.com/project/repository.grepcode.com/java/ext/com.google.android/android/
- javascript类型系统——包装对象
× 目录 [1]定义 [2]生存期 [3]显式创建[4]转型函数[5]比较运算 前面的话 javascript对象是一种复合值,它是属性或已命名值的集合.通过'.'符号来引用属性值.当属性值是一个函数 ...
- ZOJ 3804 YY's Minions (简单模拟)
/* 题意:一个矩阵中有 n*m个宠物,每一个宠物都有一个状态, 1醒着的,0睡着的 X离开的!如果这个宠物(醒着的)的周围醒着的个数>3 || <2它就会睡着, 如果这个宠物(睡着的)的 ...
- Spring之LoadTimeWeaver——一个需求引发的思考---转
原文地址:http://www.myexception.cn/software-architecture-design/602651.html Spring之LoadTimeWeaver——一个需求引 ...
- 天气webservices
Web现状 Web服务 概念 具有web服务 构成 UDDI WSDL 天气预报 web服务现状 记的以前看VB.NET时中间就介绍了WSDL,而今又学到web服务,所以用天气预报的实例学习一 ...
- java并发编程实践学习(2)--对象的组合
先验条件(Precondition):某些方法包含基于状态的先验条件.例如,不能从空队列中移除一个元素,在删除元素前队列必须处于非空状态.基于状态的先验条件的操作成为依赖状态操作. 在单线程中,如果某 ...
- redis学习教程之一基本命令
参阅redis中文的 互动教程(interactive tutorial)来学习的. 目录: 全局操作 get get incr 自增 del 删除 expire 定时 list 队列 set ...
- rtf格式的一些说明,转载的
RTF是Rich TextFormat的缩写,意即多文本格式.这是一种类似DOC格式(Word文档)的文件,有很好的兼容性,使用Windows"附件"中的"写字板&quo ...