前两篇:(列表页的动态条件搜索我是如何做列表页的)分别介绍了我们是如何做后端业务系统数据展示类的列表页以及动态搜索的,那么还剩下最重要的一项:数据展示。数据展示一般包含三部分:

  • 数据列头
  • 数据行
  • 分页统计信息,分页导航

技术依赖项:基于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来实现的列表页加载排序分页的更多相关文章

  1. 通过angularjs的directive以及service来实现的列表页加载排序分页(转)

    前两篇:(列表页的动态条件搜索,我是如何做列表页的)分别介绍了我们是如何做后端业务系统数据展示类的列表页以及动态搜索的,那么还剩下最重要的一项:数据展示.数据展示一般包含三部分: 数据列头 数据行 分 ...

  2. AngularJS进阶(三十六)AngularJS项目开发技巧之利用Service&Promise&Resolve解决图片预加载问题(后记)

    AngularJS项目开发技巧之利用Service&Promise&Resolve解决图片预加载问题(后记) 前言 在"AngularJS项目开发技巧之图片预加载" ...

  3. Windows Service 项目中 Entity Framework 无法加载的问题

    Windows Service 项目引用了别的类库项目,别的项目用到了 Entity Framework(通过Nuget引入),但是我的 Windows Service 无法开启,于是我修改了 App ...

  4. [AngularJS] 使用AngularAMD动态加载Service

    [AngularJS] 使用AngularAMD动态加载Service 前言 「使用AngularAMD动态加载Controller」:这篇文章里介绍如何使用AngularAMD来动态加载Contro ...

  5. angularJS+requireJS实现controller及directive的按需加载

    最近因为项目的比较大,需要加载的js文件较多,为了提高首屏页面的加载速度,需要对js文件进行按需加载,然后网上参考了一些资料,自己也深入研究一番之后,实现了按需加载控制器js文件及指令js文件的效果: ...

  6. AngularJS + ui-router + RequireJS异步加载注册controller/directive/filter/service

    一般情况下我们会将项目所用到的controller/directive/filter/sercive预先加载完再初始化AngularJS模块,但是当项目比较复杂的情况下,应该是打开对应的界面才加载对应 ...

  7. AngularJS中Directive指令系列 - 基本用法

    参考: https://docs.angularjs.org/api/ng/service/$compile http://www.zouyesheng.com/angular.html Direct ...

  8. AngularJs学习笔记--Managing Service Dependencies

    原版地址:http://docs.angularjs.org/guide/dev_guide.services.managing_dependencies angular允许service将其他ser ...

  9. AngularJS之directive

    AngularJS之directive AngularJS是什么就不多舌了,这里简单介绍下directive.内容基本上是读书笔记,所以如果你看过<AngularJS up and runnin ...

随机推荐

  1. WPF入门教程系列十二——依赖属性(二)

    二. 依赖属性的优先级 由于WPF 允许我们可以在多个地方设置依赖属性的值,所以我们就必须要用一个标准来保证值的优先级别.比如下面的例子中,我们在三个地方设置了按钮的背景颜色,那么哪一个设置才会是最终 ...

  2. 复杂 XML 的 序列化 反序列化

    已知.xml(再此命名default.xml)文件,请将其反序列化到一个实例对象. <?xml version="1.0" encoding="utf-8" ...

  3. hibernate(九) 二级缓存和事务级别详讲

    序言 这算是hibernate的最后一篇文章了,下一系列会讲解Struts2的东西,然后说完Struts2,在到Spring,然后在写一个SSH如何整合的案例.之后就会在去讲SSM,在之后我自己的个人 ...

  4. 队列-java代码

    public class QueueDemo { private int maxSize; private long[] queueArray; // 队列的头,实际是数组的尾 private int ...

  5. FileUpload实现文件上传(包含多文件)

    package com.hzml.serve; import java.io.File; import java.io.IOException; import java.io.PrintWriter; ...

  6. web开发者谷歌浏览器常用插件

    1.Allow-Control-Allow-Origin    安装此插件解决跨域问题,在本地起服务器可访别的域的数据. 需在Access-Control-Expose-Headers加上Allow- ...

  7. Windows Azure Web Site (15) 取消Azure Web Site默认的IIS ARR

    <Windows Azure Platform 系列文章目录> 我们知道,Azure Web Site (改名为Azure Web App)默认是可以保留Session的.Azure We ...

  8. [New Portal]Windows Azure Virtual Machine (19) 关闭Azure Virtual Machine与VIP Address,Internal IP Address的关系(1)

    <Windows Azure Platform 系列文章目录> 默认情况下,通过Azure Management Portal创建的Public IP和Private IP都是随机分配的. ...

  9. Elasticsearch入门介绍

    ES是一个高扩展的.开源的.全文检索的搜索引擎,它提供了近实时的索引.搜索.分析功能. ES文档翻译与总结参考:ES知识汇总 应用场景 1 它提供了强大的搜索功能,可以实现类似百度.谷歌等搜索. 2 ...

  10. 14个HTML5实现的效果合集

    HTML5可不是什么虚幻的概念,与其高谈阔论的讨论HTML5未来的趋势和价值,不如一起研究一下现在的HTML5可以做出哪些成果,可以让我们做出出色的产品. Form Follows Function就 ...