後台管理系統列表頁通用需求:動態條件搜索、列頭排序、分頁導航、單行操作後 Model 實時更新(無需刷新/二次請求)。技術棧:AngularJS (MVVM) + Spring MVC + HTML Table(輕量、原生支持雙向綁定)。
angular.module('app.service', ['app.constant'])
.service('$listService', function () {
var defaultOptions = {
pageSize: 10,
searchFormId: 'searchForm',
beforeSend: function () {},
callback: function ($scope, data) {},
error: function () {}
};
this.init = function ($scope, option) {
var opts = $.extend({}, defaultOptions, option);
$scope.pageSizeList = [{text:'10',value:'10'},{text:'20',value:'20'}];
$scope.pageRequest = {
pageNum: 1,
pageSize: opts.pageSize,
orderBy: '',
getResponse: function (orderBy) {
var requestData = $('#' + opts.searchFormId).serialize();
var url = opts.listUrl + '?' + requestData +
'&pageNum=' + this.pageNum +
'&pageSize=' + this.pageSize;
if (this.orderBy) url += '&orderBy=' + this.orderBy;
$.ajax({
type: 'POST', url: url, dataType: 'json', async: false,
beforeSend: opts.beforeSend, error: opts.error,
success: function (data) {
$scope.pageResponse = data;
$scope.content = data.list;
opts.callback($scope, data);
}
});
}
};
};
this.get = function () { $scope.pageRequest.getResponse(); };
});
mainApp.controller('manageCtrl', function ($scope, $listService) {
$listService.init($scope, { listUrl: '<c:url value="/theme/getAllByPage"/>' });
$listService.get();
});
angular.module('app.directives', [])
.directive('sortName', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var sortName = attrs.sortName;
var sortType = attrs.sortType || 'asc';
element.addClass('sorting').attr('sort-type', sortType);
element.on('click', function () {
var $this = $(this);
var type = $this.attr('sort-type');
scope.pageRequest.orderBy = sortName + ' ' + type;
scope.pageRequest.getResponse();
$this.removeClass('sorting sorting_asc sorting_desc')
.addClass(type === 'asc' ? 'sorting_asc' : 'sorting_desc')
.attr('sort-type', type === 'asc' ? 'desc' : 'asc');
$this.siblings('[sort-name]').removeClass('sorting_asc sorting_desc').addClass('sorting');
scope.$apply(); // 關鍵:將 Directive 隔離作用域變更傳播到父 Scope
});
}
};
});
<th class="col-md-1" sort-name="id">編號</th> <th class="col-md-4" sort-name="name">名稱</th>
<table id="datatableTheme" 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>
<span ng-show="item.status=='1'" class="label label-success">啟用</span>
<span ng-show="item.status=='0'" class="label label-danger">禁用</span>
</td>
<td ng-bind="item.description"></td>
<td>
<a ng-click="edit(item.id)"><span class="label label-primary">編輯</span></a>
<a ng-show="item.status=='0'" ng-click="enabled(item)"><span class="label label-primary">啟用</span></a>
<a ng-show="item.status=='1'" ng-click="enabled(item)"><span class="label label-primary">禁用</span></a>
</td>
</tr>
</tbody>
</table>
$scope.enabled = function (theme) {
bootbox.confirm('確認操作嗎?', function (flag) {
if (flag) {
var newStatus = theme.status === 1 ? 0 : 1;
$http.post('<c:url value="/theme/enabled"/>', {id: theme.id, status: newStatus})
.success(function (ret) {
if (ret.err) bootbox.alert(ret.err);
else {
theme.status = newStatus; // 直接修改 Model,視圖自動更新
bootbox.alert('操作成功!');
}
});
}
});
};
.directive('pagerFooter', function () {
return {
restrict: 'A',
templateUrl: '../app/template/pagerFooter.html' // 包含 ui-select 每頁條數 + uib-pagination
};
});
<div class="box-body" pager-footer></div>
ui-select 綁定 pageRequest.pageSize,ng-change 觸發重新查詢;uib-pagination 綁定 pageRequest.pageNum,ng-change 同步觸發;<meta charset="UTF-8"> 並配置 web.xml mime-mapping 解決中文亂碼。| 能力 | 本方案 | DataTables / ui-grid |
|---|---|---|
| Model 實時更新 | ✅ 原生 ng-bind | ❌ 需 API 手動刷新 |
| 復雜列邏輯/按鈕 | ✅ ng-show/click 靈活控制 | ⚠️ 需 render 回調 |
| 依賴體積 | ✅ 僅 AngularJS | ❌ 引入重型插件 |
| 嵌套表格/在線編輯 | ❌ 需自行擴展 | ✅ 內置支持 |
對於「以讀為主、單行操作多、邏輯復雜」的後台列表頁,Directive + Service + 原生 Table 是輕量、可控、擴展性強的最佳選擇。