1 ng.module('smart-table')
2 .controller('stTableController', ['$scope', '$parse', '$filter', '$attrs', function StTableController ($scope, $parse, $filter, $attrs) {
3 var propertyName = $attrs.stTable;
4 var displayGetter = $parse(propertyName);
5 var displaySetter = displayGetter.assign;
7 var orderBy = $filter('orderBy');
8 var filter = $filter('filter');
9 var safeCopy = copyRefs(displayGetter($scope));
19 var pipeAfterSafeCopy = true;
23 function copyRefs (src) {
24 return src ? [].concat(src) : [];
27 function updateSafeCopy () {
28 safeCopy = copyRefs(safeGetter($scope));
29 if (pipeAfterSafeCopy === true) {
34 function deepDelete (object, path) {
35 if (path.indexOf('.') != -1) {
36 var partials = path.split('.');
37 var key = partials.pop();
38 var parentPath = partials.join('.');
39 var parentObject = $parse(parentPath)(object)
40 delete parentObject[key];
41 if (Object.keys(parentObject).length == 0) {
42 deepDelete(object, parentPath);
49 if ($attrs.stSafeSrc) {
50 safeGetter = $parse($attrs.stSafeSrc);
51 $scope.$watch(function () {
52 var safeSrc = safeGetter($scope);
53 return safeSrc && safeSrc.length ? safeSrc[0] : undefined;
54 }, function (newValue, oldValue) {
55 if (newValue !== oldValue) {
59 $scope.$watch(function () {
60 var safeSrc = safeGetter($scope);
61 return safeSrc ? safeSrc.length : 0;
62 }, function (newValue, oldValue) {
63 if (newValue !== safeCopy.length) {
67 $scope.$watch(function () {
68 return safeGetter($scope);
69 }, function (newValue, oldValue) {
70 if (newValue !== oldValue) {
71 tableState.pagination.start = 0;
79 * @param {Function | String} predicate - function or string which will be used as predicate for the sorting
80 * @param [reverse] - if you want to reverse the order
82 this.sortBy = function sortBy (predicate, reverse) {
83 tableState.sort.predicate = predicate;
84 tableState.sort.reverse = reverse === true;
86 if (ng.isFunction(predicate)) {
87 tableState.sort.functionName = predicate.name;
89 delete tableState.sort.functionName;
92 tableState.pagination.start = 0;
97 * search matching rows
98 * @param {String} input - the input string
99 * @param {String} [predicate] - the property name against you want to check the match, otherwise it will search on all properties
101 this.search = function search (input, predicate) {
102 var predicateObject = tableState.search.predicateObject || {};
103 var prop = predicate ? predicate : '$';
105 input = ng.isString(input) ? input.trim() : input;
106 $parse(prop).assign(predicateObject, input);
107 // to avoid to filter out null value
109 deepDelete(predicateObject, prop);
111 tableState.search.predicateObject = predicateObject;
112 tableState.pagination.start = 0;
117 * this will chain the operations of sorting and filtering based on the current table state (sort options, filtering, ect)
119 this.pipe = function pipe () {
120 var pagination = tableState.pagination;
122 filtered = tableState.search.predicateObject ? filter(safeCopy, tableState.search.predicateObject) : safeCopy;
123 if (tableState.sort.predicate) {
124 filtered = orderBy(filtered, tableState.sort.predicate, tableState.sort.reverse);
126 pagination.totalItemCount = filtered.length;
127 if (pagination.number !== undefined) {
128 pagination.numberOfPages = filtered.length > 0 ? Math.ceil(filtered.length / pagination.number) : 1;
129 pagination.start = pagination.start >= filtered.length ? (pagination.numberOfPages - 1) * pagination.number : pagination.start;
130 output = filtered.slice(pagination.start, pagination.start + parseInt(pagination.number));
132 displaySetter($scope, output || filtered);
136 * select a dataRow (it will add the attribute isSelected to the row object)
137 * @param {Object} row - the row to select
138 * @param {String} [mode] - "single" or "multiple" (multiple by default)
140 this.select = function select (row, mode) {
141 var rows = copyRefs(displayGetter($scope));
142 var index = rows.indexOf(row);
144 if (mode === 'single') {
145 row.isSelected = row.isSelected !== true;
147 lastSelected.isSelected = false;
149 lastSelected = row.isSelected === true ? row : undefined;
151 rows[index].isSelected = !rows[index].isSelected;
157 * take a slice of the current sorted/filtered collection (pagination)
159 * @param {Number} start - start index of the slice
160 * @param {Number} number - the number of item in the slice
162 this.slice = function splice (start, number) {
163 tableState.pagination.start = start;
164 tableState.pagination.number = number;
169 * return the current state of the table
170 * @returns {{sort: {}, search: {}, pagination: {start: number}}}
172 this.tableState = function getTableState () {
176 this.getFilteredCollection = function getFilteredCollection () {
177 return filtered || safeCopy;
181 * Use a different filter function than the angular FilterFilter
182 * @param filterName the name under which the custom filter is registered
184 this.setFilterFunction = function setFilterFunction (filterName) {
185 filter = $filter(filterName);
189 * Use a different function than the angular orderBy
190 * @param sortFunctionName the name under which the custom order function is registered
192 this.setSortFunction = function setSortFunction (sortFunctionName) {
193 orderBy = $filter(sortFunctionName);
197 * Usually when the safe copy is updated the pipe function is called.
198 * Calling this method will prevent it, which is something required when using a custom pipe function
200 this.preventPipeOnWatch = function preventPipe () {
201 pipeAfterSafeCopy = false;
204 .directive('stTable', function () {
207 controller: 'stTableController',
208 link: function (scope, element, attr, ctrl) {
210 if (attr.stSetFilter) {
211 ctrl.setFilterFunction(attr.stSetFilter);
214 if (attr.stSetSort) {
215 ctrl.setSortFunction(attr.stSetSort);