經過angularjs的directive以及service來實現的列表頁加載排序分頁

  前兩篇:(列表頁的動態條件搜索我是如何作列表頁的)分別介紹了咱們是如何作後端業務系統數據展現類的列表頁以及動態搜索的,那麼還剩下最重要的一項:數據展現。數據展現通常包含三部分:javascript

  •   數據列頭
  •   數據行
  •   分頁統計信息,分頁導航

  技術依賴項:基於angularjs的MVVM模式,後臺是spring mvc。html


  數據表格需求:java

  •   須要支持列頭的排序
  •   須要支持單頁操做,局部更新(angular model更新),好比更新某行數據成功後,自動更新當前行的數據,而不須要刷新頁面或者另外請求後臺數據。
  •   須要支持數據邏輯運算以及複雜的html格式的數據,好比根據不一樣的數據值展現不一樣的按鈕,文本等,展現的文本須要通過特定的邏輯運算等。
  •  須要有統計信息以及分頁展現

  第三方的組件:jquery

  • jquery.datatable http://www.datatables.net/

         最終展現的結果是html table,優勢是支持嵌套表格等複雜功能,缺點是加載的文件大,且不能知足上面的需求angular model更新,對於複雜數據展現控制起來也比較複雜,須要額外的js編碼工做。git

  • angular ui-grid https://github.com/angular-ui/ui-grid/wiki/Getting-started

         最終展現的結果是div,與angularjs兼容性很好,能支持表格在線編輯,缺點一樣也是不能知足上面的需求angular model更新,對於複雜數據展現控制起來也比較複雜,須要額外的js編碼工做。

     第三方組件的特色就是功能多,但有些看起來很高級的功能咱們基本上都不用,好比在線表格數據的編輯,嵌套表格等。咱們必須的功能只有這些:排序,數據展現,分頁,若是能支持angular model更新更好。因此我決定結合html table,angularjs來完成上面的需求。

  數據加載:angularjs

   因爲咱們的數據動態查詢方案的存在,決定了大部分頁面先後臺交互的模式是相同的,因此採用angular service來提供一個listService供列表頁使用。主要是分頁大小的選擇框,以及定義了一些可配置的參數,好比執行查詢的請求地址,因爲咱們的動態查詢以表單提交,因此是將整個表單參數序列化以後再加上分頁相關信息而後提交到後端查詢。github

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,而後配置上參數便可:web

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借鑑過來,邏輯並不複雜,無非就是顯示排序標籤以及根據用戶的點擊變換排序圖標。ajax

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,值是須要排序的字段:spring

<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更新場景以及複雜列的運算以及控制。我目前還在尋找其它的組件,若是有即能知足上述的需求又使用簡單那麼也是能夠替換的,但作爲學習總結總結倒也不錯。

相關文章
相關標籤/搜索