Skip to content

Commit 0ae7a2a

Browse files
committed
closed #2, closed #8
1 parent 5c888ed commit 0ae7a2a

File tree

10 files changed

+264
-79
lines changed

10 files changed

+264
-79
lines changed

CHANGELOG.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,16 @@ Release History
33

44
## [Unreleased]
55

6-
# [1.10.0] - 2016-07-05
6+
# [1.2.0] - 2016-07-12
7+
### Added
8+
- Support for table search
9+
- Support for table filters
10+
11+
# [1.1.1] - 2016-07-06
12+
### Fixed
13+
- Totals row used old filter
14+
15+
# [1.1.0] - 2016-07-05
716
### Added
817
- Support for expanding rows i.e. open/close rows
918
- Support for compiling fields

bower.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "generic-table",
3-
"version": "1.1.0",
3+
"version": "1.2.0",
44
"main": [
55
"dist/js/generic.table.min.js",
66
"dist/css/generic-table.min.css"

directive/generic-table/generic-table.html

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,36 @@
11
<div class="generic-table">
22
<div class="gt-wrapper">
3-
<table class="table table-sortable" ng-if="gtHasData" ng-class="gtClasses">
3+
<table class="table table-sortable" ng-if="gtHasData" ng-class=":gtRefresh:gtClasses">
44
<thead>
55
<tr ng-class="::gtRowTransition ? 'fade-in animate':''">
66
<th ng-repeat="field in ::gtFields | orderBy:'columnOrder' track by field.objectKey" ng-show=":gtRefresh:gtSettings | getProperty:field.objectKey:'visible'" ng-class=":gtRefresh:[field.classNames, (field.objectKey | camelToDash) + '-column', 'sort-'+(gtSettings | getProperty:field.objectKey:'sort')]"><span ng-click=":gtRefresh:(gtSettings | getProperty:field.objectKey:'sort') === 'enable' ? sort(field.objectKey):(gtSettings | getProperty:field.objectKey:'sort') === 'asc' ? sort(field.objectKey):(gtSettings | getProperty:field.objectKey:'sort') === 'desc' ? sort(field.objectKey):''">{{::field.name}}</span></th>
77
</tr>
8-
<tr ng-if="gtTotals" ng-class="::gtRowTransition ? 'fade-in animate':''">
8+
<tr ng-if=":gtRefresh:gtTotals" ng-class="::gtRowTransition ? 'fade-in animate':''">
99
<td ng-repeat="field in ::gtFields | orderBy:'columnOrder' track by field.objectKey" class="total-column" ng-show=":gtRefresh:gtSettings | getProperty:field.objectKey:'visible'" ng-class="::[(gtFields | getProperty:field.objectKey:'classNames'), (field.objectKey | camelToDash) + '-column']" field-settings="::field" gt-render row-data="::gtTotals" gt-compile="::field.compile"></td>
1010
</tr>
1111
</thead>
1212
<tbody>
13-
<tr gt-row ng-repeat="row in gtDisplayData | limitTo: displayRows" gt-event ng-class=":gtRefresh:[gtRowTransition ? 'fade-in animate':'',row.isOpen ? 'row-open':'', $index % 2 == 0 ? 'row-odd':'row-even']">
13+
<tr gt-row ng-repeat="row in :gtRefresh:gtDisplayData | limitTo: displayRows" gt-event ng-class=":gtRefresh:[gtRowTransition ? 'fade-in animate':'',row.isOpen ? 'row-open':'', $index % 2 == 0 ? 'row-odd':'row-even']">
1414
<!--<td ng-repeat="field in ::gtFields | orderBy:'columnOrder' track by field.objectKey" ng-show=":gtRefresh:gtSettings | getProperty:field.objectKey:'visible'" ng-class="::[(gtFields | getProperty:field.objectKey:'classNames'), (field.objectKey | camelToDash) + '-column']"><span ng-class="::field.click ? 'gt-click-enabled':''" ng-bind-html="::gtFields | gtRender:row:field.objectKey" ng-click=":gtRefresh:!field.click || field.click(row);!field.expand || toggleRow(field.expand,(gtSettings | filter:{'visible':true}:true).length,row,field.objectKey);"></span></td>-->
15-
<td ng-repeat="field in gtFields | orderBy:'columnOrder' track by field.objectKey" ng-show=":gtRefresh:gtSettings | getProperty:field.objectKey:'visible'" ng-class="::[(gtFields | getProperty:field.objectKey:'classNames'), (field.objectKey | camelToDash) + '-column']"><span ng-class="::field.click ? 'gt-click-enabled':''" field-settings="::field" gt-render row-data="::row" gt-compile="::field.compile" ng-click=":gtRefresh:!field.click || field.click(row);!field.expand || toggleRow(field.expand,(gtSettings | filter:{'visible':true}:true).length,row,field.objectKey);"></span></td>
15+
<td ng-repeat="field in :gtRefresh:gtFields | orderBy:'columnOrder' track by field.objectKey" ng-show=":gtRefresh:gtSettings | getProperty:field.objectKey:'visible'" ng-class="::[(gtFields | getProperty:field.objectKey:'classNames'), (field.objectKey | camelToDash) + '-column']"><span ng-class="::field.click ? 'gt-click-enabled':''" field-settings="::field" gt-render row-data="::row" gt-compile="::field.compile" ng-click=":gtRefresh:!field.click || field.click(row);!field.expand || toggleRow(field.expand,(gtSettings | filter:{'visible':true}:true).length,row,field.objectKey);"></span></td>
1616
</tr>
1717
</tbody>
18-
<tr ng-if="pagination === false"><td class="gt-no-data" colspan="{{:gtRefresh:(gtSettings | filter:{'visible':true}:true).length}}">{{::gtNoDataTxt}}</td></t></tr>
18+
<tr ng-if=":gtRefresh:pagination === false"><td class="gt-no-data" colspan="{{:gtRefresh:(gtSettings | filter:{'visible':true}:true).length}}">{{::gtNoDataTxt}}</td></t></tr>
1919
</table>
20-
<div class="gt-pagination text-center" ng-if="gtPagination === true && pagination !== false">
20+
<div class="gt-pagination text-center" ng-if=":gtRefresh:gtPagination === true && pagination !== false">
2121
<ul class="pagination">
22-
<li ng-class="{disabled: currentPage === 0}" ng-show="currentPage !== 0">
23-
<button class="btn-link link" ng-click="previousPage()" translate="ALL.GENERAL#PAGINATION_PREVIOUS#BUTTON" ng-disabled="currentPage === 0">&laquo; Prev</button>
22+
<li ng-class=":gtRefresh:{disabled: currentPage === 0}" ng-show="currentPage !== 0">
23+
<button class="btn-link link" ng-click="previousPage()" translate="ALL.GENERAL#PAGINATION_PREVIOUS#BUTTON" ng-disabled=":gtRefresh:currentPage === 0">&laquo; Prev</button>
2424
</li>
25-
<li ng-show="currentPage > 3">
25+
<li ng-show=":gtRefresh:currentPage > 3">
2626
<button class="btn-link link" ng-click="setPage(0)">1</button><small>&hellip;</small>
2727
</li>
28-
<li style="display: inline;padding: 0 5px;" ng-repeat="page in pagination" ng-class="page === currentPage ? 'active':''"><button class="btn-link link" ng-click="setPage(page)">{{page+1}}</button></li>
29-
<li ng-show="currentPage +1 < pages.length-1 && pages.length > 4">
30-
<small ng-show="currentPage + 3 < pages.length">&hellip;</small><button class="btn-link link" ng-click="setPage(pages.length-1)">{{pages.length}}</button>
28+
<li style="display: inline;padding: 0 5px;" ng-repeat="page in :gtRefresh:pagination" ng-class=":gtRefresh:page === currentPage ? 'active':''"><button class="btn-link link" ng-click="setPage(page)">{{page+1}}</button></li>
29+
<li ng-show=":gtRefresh:currentPage +1 < pages.length-1 && pages.length > 4">
30+
<small ng-show=":gtRefresh:currentPage + 3 < pages.length">&hellip;</small><button class="btn-link link" ng-click="setPage(pages.length-1)">{{pages.length}}</button>
3131
</li>
32-
<li ng-class="{disabled: currentPage == pages.length}" ng-show="currentPage+1 !== pages.length">
33-
<button class="btn-link link" ng-click="nextPage()" translate="ALL.GENERAL#PAGINATION_NEXT#BUTTON" ng-disabled="currentPage+1 === pages.length">Next &raquo;</button>
32+
<li ng-class=":gtRefresh:{disabled: currentPage == pages.length}" ng-show=":gtRefresh:currentPage+1 !== pages.length">
33+
<button class="btn-link link" ng-click="nextPage()" translate="ALL.GENERAL#PAGINATION_NEXT#BUTTON" ng-disabled=":gtRefresh:currentPage+1 === pages.length">Next &raquo;</button>
3434
</li>
3535
</ul>
3636
</div>

directive/generic-table/generic-table.js

Lines changed: 111 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,12 @@ angular.module('generic.table').directive('genericTable', function() {
4242
}).controller('genericTableController',function($scope,$filter,$timeout, CSV,$document){
4343
var originalData; // original untouched data
4444
var mappedData; // mapped data, array containing mapped keys used by table
45+
var filteredData; // filtered mapped data
4546
var sortedData; // sorted mapped data
47+
var searchColumns; // columns to search
48+
var tableFilters; // filters applied to the table internally
49+
var searchTerms; // search terms used for searching table
50+
4651
var sorting = typeof $scope.gtSettings === 'undefined' ? false:$filter('map')($filter('filter')($scope.gtSettings,{sort:"asc desc"},function(expected, actual){
4752
return actual.indexOf(expected) > -1;
4853
}),function(sort){
@@ -52,12 +57,6 @@ angular.module('generic.table').directive('genericTable', function() {
5257
$scope.gtRows = typeof $scope.gtRows === 'undefined' ? 20:$scope.gtRows;
5358
$scope.gtNoDataTxt = typeof $scope.gtNoDataTxt === 'undefined' ? 'No table data to display':$scope.gtNoDataTxt;
5459
$scope.gtId = typeof $scope.gtId === 'undefined' ? $scope.$id:$scope.gtId;
55-
/*$scope.table = {
56-
index:$scope.gtIndex,
57-
settings: $scope.gtSettings,
58-
totals: $scope.gtTotals,
59-
fields:$scope.gtFields
60-
};*/
6160

6261
// order columns
6362
$filter('map')($scope.gtSettings,function(setting){
@@ -77,27 +76,23 @@ angular.module('generic.table').directive('genericTable', function() {
7776
$scope.gtHasData = false;
7877
originalData = initData.slice(0);
7978
$scope.$emit('gt-started-data-processing',$scope.gtId);
80-
applyFilter(initData);
79+
mapKeys(initData);
8180
};
8281

8382
// change data, this we update or data set
8483
var changeData = function(newData){
8584
$scope.gtHasData = false;
8685
originalData = newData.slice(0);
8786
$scope.$emit('gt-started-data-processing',$scope.gtId);
88-
applyFilter(newData);
89-
};
90-
91-
// internal filter, this is where we apply filters
92-
var applyFilter = function (data){
93-
var filteredData = data;
94-
mapKeys(filteredData);
95-
filteredData.length = 0;
87+
mapKeys(newData);
9688
};
9789

9890
// mapping, this is where we map which keys should be part of the table array row object
9991
var mapKeys = function (data){
10092

93+
// define which fields should be searchable
94+
searchColumns = $filter('map')($filter('removeWith')($scope.gtFields.slice(0),{search:false}),'objectKey');
95+
10196
// get key names from settings
10297
var properties = $filter('map')($filter('filter')($scope.gtSettings, {enabled:true},true),'objectKey');
10398

@@ -126,14 +121,26 @@ angular.module('generic.table').directive('genericTable', function() {
126121
});
127122
return obj;
128123
});
129-
// apply sort to our mapped array
124+
125+
applyFilter(mappedData)
126+
};
127+
128+
// internal filter, this is where we apply filters
129+
var applyFilter = function (data, search) {
130+
131+
// apply table filters
132+
var filtered = $filter('filter')(data.slice(0),tableFilters,function(expected, actual){
133+
return actual.indexOf(expected) > -1;
134+
},true).slice(0);
135+
136+
// return rows where columns match entered search terms
137+
filteredData = $filter('searchRow')(filtered,searchColumns, search);
130138
applySort();
131139
};
132140

133141
// sort, this is where we sort the filtered results
134142
var applySort = function (){
135-
sortedData = sorting !== false ? $filter('gtSort')(mappedData, sorting): mappedData;
136-
$scope.$broadcast('$$rebind::gtRefresh');
143+
sortedData = sorting !== false ? $filter('gtSort')(filteredData, sorting): filteredData;
137144
applyPagination();
138145
};
139146

@@ -145,7 +152,6 @@ angular.module('generic.table').directive('genericTable', function() {
145152
$scope.$emit('gt-started-rendering');
146153
$scope.gtDisplayData = $scope.pages[$scope.currentPage];
147154
pagination($scope.pages.length,$scope.currentPage);
148-
149155
$timeout(function(){
150156
$scope.$emit('gt-finished-data-processing',$scope.gtId);
151157
$scope.gtHasData = true;
@@ -156,17 +162,31 @@ angular.module('generic.table').directive('genericTable', function() {
156162
$scope.$emit('gt-finished-rendering');
157163
$scope.loading = false;
158164
}*/
165+
$scope.$broadcast('$$rebind::gtRefresh');
159166
};
160167

161168
// listen for update table events
162169
$scope.$on('gt-update-table:'+$scope.gtId,function(event,arg){
163170
changeData(arg);
164171
});
165172

173+
// listen for search events
174+
$scope.$on('gt-search-table:'+$scope.gtId,function(event,arg){
175+
searchTerms = arg;
176+
applyFilter(mappedData.slice(0),arg);
177+
});
178+
179+
// listen for filter events
180+
$scope.$on('gt-filter-table:'+$scope.gtId,function(event,arg){
181+
tableFilters = arg;
182+
applyFilter(mappedData.slice(0),searchTerms);
183+
});
184+
166185
// listen for update table events
167186
$scope.$on('gt-update-structure:'+$scope.gtId,function(event,arg){
168187
$scope.gtFields = arg.fields;
169188
$scope.gtSettings = arg.settings;
189+
searchColumns = $filter('map')($filter('removeWith')($scope.gtFields.slice(0),{search:false}),'objectKey');
170190

171191
// if no sorting is applied or if sorting is forced...
172192
if(sorting === false || arg.forceSorting === true) {
@@ -215,6 +235,15 @@ angular.module('generic.table').directive('genericTable', function() {
215235
// if total pages equals 0 ie. no data available
216236
if(totalPages === 0 ) {
217237
$scope.pagination = false;
238+
$scope.$emit('gt-table-filtered', {
239+
total:mappedData.length,
240+
filtered:0,
241+
showingFrom:0,
242+
showingTo:0,
243+
pageLength:$scope.gtRows,
244+
currentPage:0,
245+
numberOfPages:0
246+
});
218247
return;
219248
}
220249

@@ -250,31 +279,52 @@ angular.module('generic.table').directive('genericTable', function() {
250279
else if(totalPages-4 > currentPage){
251280
$scope.pagination = [currentPage-1,currentPage,currentPage+1, currentPage+2];
252281
}
282+
283+
$scope.$emit('gt-table-filtered', {
284+
total:mappedData.length,
285+
filtered:filteredData.length,
286+
showingFrom:$scope.currentPage*$scope.gtRows+($scope.currentPage > 1 ? 0:1),
287+
showingTo:$scope.currentPage*$scope.gtRows+$scope.gtDisplayData.length,
288+
pageLength:$scope.gtRows,
289+
currentPage:$scope.currentPage,
290+
numberOfPages:$scope.pages.length
291+
});
253292
};
254293

255294
$scope.nextPage = function(){
256295
try {
257296
$scope.currentPage ++;
297+
$scope.gtDisplayData = $scope.pages[$scope.currentPage];
258298
pagination($scope.pages.length,$scope.currentPage);
259299
$scope.$emit('gt-started-rendering',$scope.gtId);
260-
$scope.gtDisplayData = $scope.pages[$scope.currentPage];
300+
$scope.$broadcast('$$rebind::gtRefresh');
261301
} catch(error) {
262302
console.log(error);
263303
}
264304

265305
};
266306

267307
$scope.previousPage = function(){
268-
$scope.currentPage --;
269-
pagination($scope.pages.length,$scope.currentPage);
270-
$scope.$emit('gt-started-rendering',$scope.gtId);
271-
$scope.gtDisplayData = $scope.pages[$scope.currentPage];
308+
try {
309+
$scope.currentPage --;
310+
$scope.gtDisplayData = $scope.pages[$scope.currentPage];
311+
pagination($scope.pages.length,$scope.currentPage);
312+
$scope.$emit('gt-started-rendering',$scope.gtId);
313+
$scope.$broadcast('$$rebind::gtRefresh');
314+
} catch(error) {
315+
console.log(error);
316+
}
272317
};
273318
$scope.setPage = function(page){
274-
$scope.currentPage = page;
275-
pagination($scope.pages.length,$scope.currentPage);
276-
$scope.$emit('gt-started-rendering',$scope.gtId);
277-
$scope.gtDisplayData = $scope.pages[$scope.currentPage];
319+
try {
320+
$scope.currentPage = page;
321+
$scope.gtDisplayData = $scope.pages[$scope.currentPage];
322+
pagination($scope.pages.length,$scope.currentPage);
323+
$scope.$emit('gt-started-rendering',$scope.gtId);
324+
$scope.$broadcast('$$rebind::gtRefresh');
325+
} catch(error) {
326+
console.log(error);
327+
}
278328
};
279329

280330
// sort function
@@ -418,9 +468,7 @@ angular.module('generic.table').directive('genericTable', function() {
418468
restrict: 'A',
419469
scope:false,
420470
link: function(scope, element, attrs, fn) {
421-
scope.row.isOpen;
422471
scope.toggleRow = function(content,columns,row,key){
423-
//console.log(isOpen,content,columns);
424472
if(!scope.row.isOpen){
425473
var row = $compile('<tr class="expanded-row"><td colspan="'+columns+'">'+content+'</td></tr>')(scope);
426474
element.after(row);
@@ -430,19 +478,7 @@ angular.module('generic.table').directive('genericTable', function() {
430478
scope.row.isOpen = false;
431479
}
432480
scope.$broadcast('$$rebind::gtRefresh'); // update bindings
433-
//isOpen != isOpen;
434-
//console.log(isOpen);
435481
};
436-
/*scope.openRow = function(){
437-
var row = '<tr><td colspan="4">Test</td></tr>';
438-
element.after(row);
439-
};
440-
scope.closeRow = function(){
441-
element.next().remove();
442-
};
443-
scope.$on('gt-open-row',function(){
444-
console.log('open row');
445-
});*/
446482
}
447483
};
448484
}).directive('gtEvent', function() {
@@ -491,6 +527,39 @@ angular.module('generic.table').directive('genericTable', function() {
491527
}
492528
return output;
493529
}
530+
}).filter('searchRow',function($filter){
531+
return function(array,fields,searchTerms){
532+
533+
var filteredArray = [];
534+
var searchTerms = typeof searchTerms === 'undefined' ? '':searchTerms;
535+
var searchTermsArray = searchTerms.toLowerCase().split(' ');
536+
537+
for (var i = 0; i < array.length; i++){
538+
var row = array[i];
539+
var string = '';
540+
$filter('map')(fields,function(field){
541+
string += row[field];
542+
});
543+
string = string.toLowerCase();
544+
var match = true;
545+
for (var k = 0; k < searchTermsArray.length;k++){
546+
var term = searchTermsArray[k];
547+
match = string.indexOf(term) !== -1;
548+
549+
if(!match){
550+
break;
551+
// no match
552+
//return match;
553+
554+
}
555+
}
556+
if(match){
557+
//console.log('push',row);
558+
filteredArray.push(row)
559+
}
560+
}
561+
return filteredArray;
562+
}
494563
}).filter('camelToDash',function(){
495564
return function(string){try{
496565
return string.replace( /([a-z])([A-Z])/g, '$1-$2' ).toLowerCase()} catch(error){console.log('nothing to replace:', error)};

index-dev.html

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
<script src="bower_components/ng-csv/build/ng-csv.min.js"></script>
3232
<!-- Add New Bower Component JS Above -->
3333

34-
3534
<!-- Main App JS -->
3635
<script src="app.js"></script>
3736
<script src="directive/generic-table/generic-table.js"></script>

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "generic-table",
3-
"version": "1.1.0",
3+
"version": "1.2.0",
44
"devDependencies": {
55
"bower": "1.6.2",
66
"bower-art-resolver": "^2.0.1",

partial/dev/dev.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<div class="col-md-12" ng-controller="DevController">
22
<button class="btn btn-default" ng-click="exportCsv()">Export</button>
3-
<generic-table gt-classes="table-bordered table-condensed table-hover" gt-id="basicTableId" gt-data="basicTable.data" gt-settings="basicTable.settings" gt-fields="basicTable.fields" gt-totals="basicTable.totals" gt-rows="5"></generic-table>
3+
<input ng-model="searchField" ng-change="searchTable(searchField)"/>
4+
<generic-table gt-classes="table-bordered table-condensed table-hover" gt-id="basicTableId" gt-data="basicTable.data" gt-settings="basicTable.settings" gt-fields="basicTable.fields" gt-totals="basicTable.totals" gt-rows="6"></generic-table>
45
</div>

0 commit comments

Comments
 (0)