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

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

技术依赖项:基于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更新场景以及复杂列的运算以及控制。我目前还在寻找其它的组件,如果有即能满足上述的需求又使用简单那么也是可以替换的,但做为学习总结总结倒也不错。

http://www.cnblogs.com/ASPNET2008/p/5311979.html

通过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. Dom4j SAXReader Constructors

    Dom4j读取xml:eg1: package xml; import java.io.File; import org.dom4j.DocumentException; import org.dom ...

  2. Oracle百问百答(一)

    Oracle百问百答(一) 01.如何查看oracle的版本信息? 02.如何查看系统被锁的事务信息? 03.怎么获取有哪些用户在使用数据库? 04. 数据表中的字段最大数是多少? 表或视图中的最大列 ...

  3. drupal THEME主要文件

    **.info 文件** .info 文件是一个必需的文件:Drupal 必须包括它,才干看到主题. .info 文件告诉 Drupal 主题的内部名称.比如,假设这个文件的名称是 ibmtheme. ...

  4. [docker]coreOS与atomic对照

    声明: 本博客欢迎转发,但请保留原作者信息! 博客地址:http://blog.csdn.net/halcyonbaby 内容系本人学习.研究和总结,如有雷同,实属荣幸! 摘自https://majo ...

  5. java之jvm学习笔记四(安全管理器)

    java之jvm学习笔记四(安全管理器) 前面已经简述了java的安全模型的两个组成部分(类装载器,class文件校验器),接下来学习的是java安全模型的另外一个重要组成部分安全管理器. 安全管理器 ...

  6. js技术要点---JS 获取网页源代码

    JS 获取网页源代码 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html& ...

  7. 基于三星I9250演示自己弄的Miracast功能-手机对手机

    最终把Miracast功能測试通了,为了节省时间.我的Source端和Sink端都採用同一个机型.这样能够降低我为目标机编译4.4.2源码所耗费的时间.今天简单录制了一段视频.视频是用手机录制的,室内 ...

  8. CoreJava_Collection接口add有一个返回值!

        今天讨论与朋友小知识,我发现很多人不知道collection接口定义add方法返回的值,我们所有的工作很多年Java职工.不熟悉的公共接口,使自己成为在细节上的麻烦.一个小采访这让我想起,当我 ...

  9. hdu1251(Trie树)

    传送门:统计难题 分析:Trie树入门题,随便写写练下手感,统计每个节点被多少单词经过就可以了. #include <iostream> #include <cstdio> # ...

  10. loj1201(最大独立集)

    传送门:A Perfect Murder 题意:有一群苍蝇,之间有一些是朋友关系,如果杀了一只苍蝇,那么它的朋友们都会有警惕性,再也杀不了这些朋友了,问最多能杀多少只苍蝇. 分析:根据朋友性连边,最多 ...