@bennerinformatics/ember-fw-table 2.0.16 → 2.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,470 +1,470 @@
1
- import Component from '@ember/component';
2
- import {computed} from '@ember/object';
3
- import {empty, filterBy} from '@ember/object/computed';
4
- import {inject} from '@ember/service';
5
- import {isEmpty, isNone} from '@ember/utils';
6
- import {handleAjaxError} from '@bennerinformatics/ember-fw/utils/error';
7
- import exportTable from '@bennerinformatics/ember-fw-table/utils/export';
8
- import Table from 'ember-light-table';
9
- import RSVP from 'rsvp';
10
- import layout from '../templates/components/fw-pagination-wrapper';
11
-
12
- export default Component.extend({
13
- layout,
14
- ajax: inject(),
15
- config: inject(),
16
- media: inject(),
17
- notifications: inject(),
18
- store: inject(),
19
- tagName: '',
20
-
21
- /*
22
- * Parameters
23
- */
24
-
25
- /**
26
- * Name of the model in search
27
- * @type {String}
28
- * @prop modelName
29
- */
30
-
31
- /**
32
- * Default sort order for the table
33
- * @type {String}
34
- */
35
- defaultSortKey: null,
36
-
37
- /**
38
- * Number of entries per page
39
- * @type {Number}
40
- */
41
- entriesPerPage: 100,
42
-
43
- /**
44
- * Gets the title of this table at the given time
45
- * @return {String} Title of this table
46
- */
47
- getTitle() {
48
- return 'Table';
49
- },
50
-
51
- /**
52
- * Gets a list of table columns for exporting
53
- * @return {Array} Array of table columns
54
- */
55
- getExportColumns() {},
56
-
57
- /**
58
- * Called when the table search button is pressed
59
- */
60
- onSearch() {},
61
-
62
- /**
63
- * Called to delete the full page of entries
64
- * Should be passed in with a function
65
- */
66
- deletePage: undefined,
67
-
68
- /**
69
- * Determines permission for deleteTablePermission for fw-table-sortable
70
- */
71
- deletePagePermission: true,
72
- /**
73
- * Makes a query object based on the search fields
74
- * @param {Boolean} count If true, counting
75
- * @param {Number} page If defined, page number for a page search
76
- * @param {Boolean} export If true, exporting
77
- * @return {Object} Query object
78
- */
79
- makeQuery(/* {count, page, export} */) {
80
- return {};
81
- },
82
-
83
- /* Table generation properties */
84
-
85
- /**
86
- * Gets a list of table columns for the results. If null, yields for table
87
- * @return {Array} Array of table columns
88
- */
89
- getTableColumns() {
90
- return null;
91
- },
92
-
93
- /**
94
- * Text to show for empty tables. If null, hides on empty
95
- * @type {String}
96
- */
97
- emptyText: null,
98
-
99
- /**
100
- * Actions to pass into the table
101
- * @type {Object}
102
- */
103
- tableActions: null,
104
-
105
- /**
106
- * Class names for the wrapper around the loading spinner and the table
107
- * @type {String}
108
- */
109
- tableWrapperClass: '',
110
-
111
- /*
112
- * Search properties
113
- */
114
-
115
- /**
116
- * Table title at this time
117
- * @type {String}
118
- */
119
- currentTitle: null,
120
-
121
- /**
122
- * If true, currently doing the main search
123
- * @type {Boolean}
124
- */
125
- searchingTable: false,
126
-
127
- /**
128
- * If true, show export page button
129
- * @type {Boolean}
130
- */
131
- showExport: true,
132
- /**
133
- * Number of pages being searched
134
- * @type {Number}
135
- */
136
- pagesSearching: 0,
137
-
138
- /**
139
- * Array of entries, indexes are the pages
140
- * @type {Array}
141
- */
142
- pageEntries: null,
143
-
144
- /**
145
- * Currently selected page
146
- * @type {Number}
147
- */
148
- page: 1,
149
-
150
- /**
151
- * Total number of entries
152
- * @type {Number}
153
- */
154
- count: null,
155
-
156
- /**
157
- * Query last time createQuery was called
158
- * @type {[type]}
159
- */
160
- lastQuery: null,
161
-
162
- /**
163
- * Current sort order for the table
164
- * @type {String}
165
- * @prop currentSortKey
166
- */
167
- currentSortKey: null,
168
-
169
- /*
170
- * Computed properties
171
- */
172
-
173
- /**
174
- * Array index for the selected page
175
- * @type {Number}
176
- */
177
- index: computed('page', function() {
178
- return this.get('page') - 1;
179
- }),
180
-
181
- /**
182
- * Array of entries at the current page
183
- * @type {Array}
184
- */
185
- currentEntries: computed('pageEntries.[]', 'index', function() {
186
- let entries = this.get('pageEntries');
187
- if (isNone(entries)) {
188
- return [];
189
- }
190
- return entries.objectAt(this.get('index'));
191
- }),
192
-
193
- /**
194
- * Filtered entries, removing deleted entries
195
- * @type {Array}
196
- */
197
- filteredEntries: filterBy('currentEntries', 'isDeleted', false),
198
-
199
- /**
200
- * Current table sort key based on given properties
201
- * @type {String}
202
- */
203
- tableSortKey: computed('defaultSortKey', 'currentSortKey', function() {
204
- let current = this.get('currentSortKey');
205
- if (isEmpty(current)) {
206
- return `${this.get('defaultSortKey')}:desc`;
207
- }
208
- return current;
209
- }),
210
-
211
- /**
212
- * Number of pages available
213
- * @type {Number}
214
- */
215
- totalPages: computed('count', 'entriesPerPage', function() {
216
- let count = this.get('count');
217
- if (isNone(count)) {
218
- return 0;
219
- }
220
-
221
- return Math.ceil(count / this.get('entriesPerPage'));
222
- }),
223
-
224
- /**
225
- * True if we have multiple pages
226
- * @type {Boolean}
227
- */
228
- showPages: computed('totalPages', function() {
229
- return this.get('totalPages') > 1;
230
- }),
231
-
232
- /**
233
- * Maximum number of pages to show in the pagination component
234
- * @type {Number}
235
- */
236
- maxPageButtons: computed('media.{isMobile,isTablet}', function() {
237
- let media = this.get('media');
238
- if (media.get('isMobile')) {
239
- return 3;
240
- }
241
- if (media.get('isTablet')) {
242
- return 5;
243
- }
244
-
245
- return 7;
246
- }),
247
-
248
- /**
249
- * Gets the serverside route to use for this model name
250
- * @type {String}
251
- */
252
- routeName: computed('modelName', function() {
253
- let name = this.get('modelName');
254
- return this.get('store').adapterFor(name).pathForType(name);
255
- }),
256
-
257
- /**
258
- * Final piece of title for table
259
- * @type {String}
260
- */
261
- tableSuffix: computed('count', 'currentEntries', 'index', function() {
262
- let count = this.get('count');
263
- let entries = this.get('currentEntries');
264
- if (isEmpty(entries)) {
265
- return `${count} entries`;
266
- }
267
- return `${entries.get('length')} of ${count} entries`;
268
- }),
269
-
270
- /**
271
- * Title for the table
272
- * @type {String}
273
- */
274
- fullTableTitle: computed('currentTitle', 'tableSuffix', function() {
275
- return `${this.get('currentTitle')} - ${this.get('tableSuffix')}`;
276
- }),
277
-
278
- /** If true, hide the table when empty */
279
- hideEmpty: empty('emptyText'),
280
-
281
- /* Functions */
282
-
283
- /**
284
- * Queries the serverside to get the total record count
285
- * @return {Promise} Promise that resolves to a number
286
- */
287
- queryCount() {
288
- // fetch standard query
289
- let query = this.makeQuery({count: true});
290
- query.count = true;
291
-
292
- // make request
293
- let url = this.get('config').formUrl(this.get('routeName'));
294
- return this.get('ajax').request(url, {data: query}).then((({count}) => count)).catch(handleAjaxError.bind(this));
295
- },
296
-
297
- /**
298
- * Query for settign a new sort order
299
- * @param {Number} page Page number to start
300
- * @param {String} sortKey New sort order
301
- * @param {Boolean} ascending If true, sorts ascending, false descending
302
- * @return {Promise} Promise that resolves to a entry array
303
- */
304
- querySort(page, sortKey, ascending) {
305
- let query = this.get('lastQuery');
306
-
307
- // set sort key stuff if present
308
- if (!isNone(ascending)) {
309
- query.ascending = ascending;
310
- }
311
- if (!isEmpty(sortKey)) {
312
- query.sortKey = sortKey;
313
- }
314
-
315
- // set limits on query
316
- let entriesPerPage = this.get('entriesPerPage');
317
- query.limit = entriesPerPage;
318
- query.offset = (page - 1) * entriesPerPage;
319
-
320
- // make promise
321
- return RSVP.resolve(this.get('store').query(this.get('modelName'), query)).catch(handleAjaxError.bind(this));
322
- },
323
-
324
- /**
325
- * Fetches the entries for the given page number
326
- * @param {Number} page Page to fetch
327
- * @return {Promise} Promise that resolves to an entry array
328
- */
329
- queryPage(page) {
330
- // same as sort, but handles the entries
331
- return this.querySort(page).then((entries) => {
332
- this.get('pageEntries')[page - 1] = entries;
333
- return entries;
334
- });
335
- },
336
-
337
- /**
338
- * Gets all entries for the given query
339
- * @return {Promise} Promise that resolves to entries
340
- */
341
- queryAll() {
342
- let query = this.makeQuery({export: true});
343
- return RSVP.resolve(this.get('store').query(this.get('modelName'), query)).catch(handleAjaxError.bind(this));
344
- },
345
-
346
- actions: {
347
- /* Search buttons */
348
-
349
- /**
350
- * Called when the search button is pressed
351
- * @return {Promise} Promise that resolves after searching
352
- */
353
- search() {
354
- // TODO: canSearch?
355
- // else a title getter
356
-
357
- // start search and clean up old data
358
- this.setProperties({
359
- currentTitle: this.getTitle(),
360
- // search data
361
- lastQuery: this.makeQuery({page: 1}),
362
- pageEntries: [],
363
- page: 1,
364
- // searching keys
365
- searchingTable: true,
366
- pagesSearching: 0,
367
- tableColumns: this.getTableColumns()
368
- });
369
-
370
- // search callback
371
- this.onSearch();
372
-
373
- // make two requests: one for the total count and one for the first 100 entries
374
- return RSVP.hash({
375
- count: this.queryCount(),
376
- entries: this.queryPage(1)
377
- }).then(({count}) => {
378
- // entries already set as part of queryPage
379
- this.setProperties({
380
- count,
381
- searchingTable: false
382
- });
383
- }).catch(() => {
384
- // request failed, clean up data
385
- this.setProperties({
386
- // search data
387
- pageEntries: null,
388
- count: 0,
389
- // searching keys
390
- searchingTable: false
391
- });
392
- });
393
- },
394
-
395
- /**
396
- * Called when the export button is clicked to export all data
397
- * @return {Promise} Promise that resolves after exporting the table
398
- */
399
- export() {
400
- // TODO: canSearch?
401
-
402
- // build table for export
403
- let table = new Table(this.getExportColumns());
404
- return this.queryAll().then((entries) => {
405
- table.setRows(entries.sortBy(this.get('defaultSortKey')).reverse());
406
- exportTable(table, `${this.getTitle()} - All Entries`);
407
- }).catch(handleAjaxError.bind(this));
408
- },
409
-
410
- /* Pagination */
411
-
412
- /**
413
- * Called when a page button is clicked to switch pages
414
- * @param {Number} page New page number to set
415
- */
416
- setPage(page) {
417
- // clamp page number
418
- let max = this.get('totalPages');
419
- if (page < 1) {
420
- page = 1;
421
- } else if (page > max) {
422
- page = max;
423
- }
424
-
425
- // if we havve entries at the page number, use those
426
- // if missing, query them
427
- if (isNone(this.get('pageEntries').objectAt(page - 1))) {
428
- this.incrementProperty('pagesSearching');
429
- this.set('page', page);
430
- this.queryPage(page).then(() => {
431
- // entries set in promise logic
432
- this.decrementProperty('pagesSearching');
433
- });
434
- } else {
435
- this.set('page', page);
436
- }
437
- },
438
-
439
- /* Sorting */
440
-
441
- /**
442
- * Resorts the entry by the given column
443
- * @param {Column} column Column to use for sorting
444
- * @param {String} sortKey String to use for sorting in the column
445
- * @return {Promise} Promise that resolves to entries
446
- */
447
- sortColumn(column, sortKey) {
448
- // if the sort key is unchanged, do nothing
449
- if (sortKey === this.get('tableSortKey')) {
450
- return RSVP.resolve();
451
- }
452
-
453
- // mark that we are sorting, column properties do not add desc
454
- column.set('sorting', true);
455
-
456
- // search for data
457
- let page = this.get('page');
458
- return this.querySort(page, column.searchKey || column.valuePath, column.ascending).then((entries) => {
459
- // set entries to new list
460
- let pageEntries = [];
461
- pageEntries[page - 1] = entries;
462
- this.setProperties({pageEntries, currentSortKey: sortKey});
463
-
464
- // mark that we are done sorting
465
- column.set('sorting', false);
466
- return entries;
467
- });
468
- }
469
- }
470
- });
1
+ import Component from '@ember/component';
2
+ import {computed} from '@ember/object';
3
+ import {empty, filterBy} from '@ember/object/computed';
4
+ import {inject} from '@ember/service';
5
+ import {isEmpty, isNone} from '@ember/utils';
6
+ import {handleAjaxError} from '@bennerinformatics/ember-fw/utils/error';
7
+ import exportTable from '@bennerinformatics/ember-fw-table/utils/export';
8
+ import Table from 'ember-light-table';
9
+ import RSVP from 'rsvp';
10
+ import layout from '../templates/components/fw-pagination-wrapper';
11
+
12
+ export default Component.extend({
13
+ layout,
14
+ ajax: inject(),
15
+ config: inject(),
16
+ media: inject(),
17
+ notifications: inject(),
18
+ store: inject(),
19
+ tagName: '',
20
+
21
+ /*
22
+ * Parameters
23
+ */
24
+
25
+ /**
26
+ * Name of the model in search
27
+ * @type {String}
28
+ * @prop modelName
29
+ */
30
+
31
+ /**
32
+ * Default sort order for the table
33
+ * @type {String}
34
+ */
35
+ defaultSortKey: null,
36
+
37
+ /**
38
+ * Number of entries per page
39
+ * @type {Number}
40
+ */
41
+ entriesPerPage: 100,
42
+
43
+ /**
44
+ * Gets the title of this table at the given time
45
+ * @return {String} Title of this table
46
+ */
47
+ getTitle() {
48
+ return 'Table';
49
+ },
50
+
51
+ /**
52
+ * Gets a list of table columns for exporting
53
+ * @return {Array} Array of table columns
54
+ */
55
+ getExportColumns() {},
56
+
57
+ /**
58
+ * Called when the table search button is pressed
59
+ */
60
+ onSearch() {},
61
+
62
+ /**
63
+ * Called to delete the full page of entries
64
+ * Should be passed in with a function
65
+ */
66
+ deletePage: undefined,
67
+
68
+ /**
69
+ * Determines permission for deleteTablePermission for fw-table-sortable
70
+ */
71
+ deletePagePermission: true,
72
+ /**
73
+ * Makes a query object based on the search fields
74
+ * @param {Boolean} count If true, counting
75
+ * @param {Number} page If defined, page number for a page search
76
+ * @param {Boolean} export If true, exporting
77
+ * @return {Object} Query object
78
+ */
79
+ makeQuery(/* {count, page, export} */) {
80
+ return {};
81
+ },
82
+
83
+ /* Table generation properties */
84
+
85
+ /**
86
+ * Gets a list of table columns for the results. If null, yields for table
87
+ * @return {Array} Array of table columns
88
+ */
89
+ getTableColumns() {
90
+ return null;
91
+ },
92
+
93
+ /**
94
+ * Text to show for empty tables. If null, hides on empty
95
+ * @type {String}
96
+ */
97
+ emptyText: null,
98
+
99
+ /**
100
+ * Actions to pass into the table
101
+ * @type {Object}
102
+ */
103
+ tableActions: null,
104
+
105
+ /**
106
+ * Class names for the wrapper around the loading spinner and the table
107
+ * @type {String}
108
+ */
109
+ tableWrapperClass: '',
110
+
111
+ /*
112
+ * Search properties
113
+ */
114
+
115
+ /**
116
+ * Table title at this time
117
+ * @type {String}
118
+ */
119
+ currentTitle: null,
120
+
121
+ /**
122
+ * If true, currently doing the main search
123
+ * @type {Boolean}
124
+ */
125
+ searchingTable: false,
126
+
127
+ /**
128
+ * If true, show export page button
129
+ * @type {Boolean}
130
+ */
131
+ showExport: true,
132
+ /**
133
+ * Number of pages being searched
134
+ * @type {Number}
135
+ */
136
+ pagesSearching: 0,
137
+
138
+ /**
139
+ * Array of entries, indexes are the pages
140
+ * @type {Array}
141
+ */
142
+ pageEntries: null,
143
+
144
+ /**
145
+ * Currently selected page
146
+ * @type {Number}
147
+ */
148
+ page: 1,
149
+
150
+ /**
151
+ * Total number of entries
152
+ * @type {Number}
153
+ */
154
+ count: null,
155
+
156
+ /**
157
+ * Query last time createQuery was called
158
+ * @type {[type]}
159
+ */
160
+ lastQuery: null,
161
+
162
+ /**
163
+ * Current sort order for the table
164
+ * @type {String}
165
+ * @prop currentSortKey
166
+ */
167
+ currentSortKey: null,
168
+
169
+ /*
170
+ * Computed properties
171
+ */
172
+
173
+ /**
174
+ * Array index for the selected page
175
+ * @type {Number}
176
+ */
177
+ index: computed('page', function() {
178
+ return this.get('page') - 1;
179
+ }),
180
+
181
+ /**
182
+ * Array of entries at the current page
183
+ * @type {Array}
184
+ */
185
+ currentEntries: computed('pageEntries.[]', 'index', function() {
186
+ let entries = this.get('pageEntries');
187
+ if (isNone(entries)) {
188
+ return [];
189
+ }
190
+ return entries.objectAt(this.get('index'));
191
+ }),
192
+
193
+ /**
194
+ * Filtered entries, removing deleted entries
195
+ * @type {Array}
196
+ */
197
+ filteredEntries: filterBy('currentEntries', 'isDeleted', false),
198
+
199
+ /**
200
+ * Current table sort key based on given properties
201
+ * @type {String}
202
+ */
203
+ tableSortKey: computed('defaultSortKey', 'currentSortKey', function() {
204
+ let current = this.get('currentSortKey');
205
+ if (isEmpty(current)) {
206
+ return `${this.get('defaultSortKey')}:desc`;
207
+ }
208
+ return current;
209
+ }),
210
+
211
+ /**
212
+ * Number of pages available
213
+ * @type {Number}
214
+ */
215
+ totalPages: computed('count', 'entriesPerPage', function() {
216
+ let count = this.get('count');
217
+ if (isNone(count)) {
218
+ return 0;
219
+ }
220
+
221
+ return Math.ceil(count / this.get('entriesPerPage'));
222
+ }),
223
+
224
+ /**
225
+ * True if we have multiple pages
226
+ * @type {Boolean}
227
+ */
228
+ showPages: computed('totalPages', function() {
229
+ return this.get('totalPages') > 1;
230
+ }),
231
+
232
+ /**
233
+ * Maximum number of pages to show in the pagination component
234
+ * @type {Number}
235
+ */
236
+ maxPageButtons: computed('media.{isMobile,isTablet}', function() {
237
+ let media = this.get('media');
238
+ if (media.get('isMobile')) {
239
+ return 3;
240
+ }
241
+ if (media.get('isTablet')) {
242
+ return 5;
243
+ }
244
+
245
+ return 7;
246
+ }),
247
+
248
+ /**
249
+ * Gets the serverside route to use for this model name
250
+ * @type {String}
251
+ */
252
+ routeName: computed('modelName', function() {
253
+ let name = this.get('modelName');
254
+ return this.get('store').adapterFor(name).pathForType(name);
255
+ }),
256
+
257
+ /**
258
+ * Final piece of title for table
259
+ * @type {String}
260
+ */
261
+ tableSuffix: computed('count', 'currentEntries', 'index', function() {
262
+ let count = this.get('count');
263
+ let entries = this.get('currentEntries');
264
+ if (isEmpty(entries)) {
265
+ return `${count} entries`;
266
+ }
267
+ return `${entries.get('length')} of ${count} entries`;
268
+ }),
269
+
270
+ /**
271
+ * Title for the table
272
+ * @type {String}
273
+ */
274
+ fullTableTitle: computed('currentTitle', 'tableSuffix', function() {
275
+ return `${this.get('currentTitle')} - ${this.get('tableSuffix')}`;
276
+ }),
277
+
278
+ /** If true, hide the table when empty */
279
+ hideEmpty: empty('emptyText'),
280
+
281
+ /* Functions */
282
+
283
+ /**
284
+ * Queries the serverside to get the total record count
285
+ * @return {Promise} Promise that resolves to a number
286
+ */
287
+ queryCount() {
288
+ // fetch standard query
289
+ let query = this.makeQuery({count: true});
290
+ query.count = true;
291
+
292
+ // make request
293
+ let url = this.get('config').formUrl(this.get('routeName'));
294
+ return this.get('ajax').request(url, {data: query}).then((({count}) => count)).catch(handleAjaxError.bind(this));
295
+ },
296
+
297
+ /**
298
+ * Query for settign a new sort order
299
+ * @param {Number} page Page number to start
300
+ * @param {String} sortKey New sort order
301
+ * @param {Boolean} ascending If true, sorts ascending, false descending
302
+ * @return {Promise} Promise that resolves to a entry array
303
+ */
304
+ querySort(page, sortKey, ascending) {
305
+ let query = this.get('lastQuery');
306
+
307
+ // set sort key stuff if present
308
+ if (!isNone(ascending)) {
309
+ query.ascending = ascending;
310
+ }
311
+ if (!isEmpty(sortKey)) {
312
+ query.sortKey = sortKey;
313
+ }
314
+
315
+ // set limits on query
316
+ let entriesPerPage = this.get('entriesPerPage');
317
+ query.limit = entriesPerPage;
318
+ query.offset = (page - 1) * entriesPerPage;
319
+
320
+ // make promise
321
+ return RSVP.resolve(this.get('store').query(this.get('modelName'), query)).catch(handleAjaxError.bind(this));
322
+ },
323
+
324
+ /**
325
+ * Fetches the entries for the given page number
326
+ * @param {Number} page Page to fetch
327
+ * @return {Promise} Promise that resolves to an entry array
328
+ */
329
+ queryPage(page) {
330
+ // same as sort, but handles the entries
331
+ return this.querySort(page).then((entries) => {
332
+ this.get('pageEntries')[page - 1] = entries;
333
+ return entries;
334
+ });
335
+ },
336
+
337
+ /**
338
+ * Gets all entries for the given query
339
+ * @return {Promise} Promise that resolves to entries
340
+ */
341
+ queryAll() {
342
+ let query = this.makeQuery({export: true});
343
+ return RSVP.resolve(this.get('store').query(this.get('modelName'), query)).catch(handleAjaxError.bind(this));
344
+ },
345
+
346
+ actions: {
347
+ /* Search buttons */
348
+
349
+ /**
350
+ * Called when the search button is pressed
351
+ * @return {Promise} Promise that resolves after searching
352
+ */
353
+ search() {
354
+ // TODO: canSearch?
355
+ // else a title getter
356
+
357
+ // start search and clean up old data
358
+ this.setProperties({
359
+ currentTitle: this.getTitle(),
360
+ // search data
361
+ lastQuery: this.makeQuery({page: 1}),
362
+ pageEntries: [],
363
+ page: 1,
364
+ // searching keys
365
+ searchingTable: true,
366
+ pagesSearching: 0,
367
+ tableColumns: this.getTableColumns()
368
+ });
369
+
370
+ // search callback
371
+ this.onSearch();
372
+
373
+ // make two requests: one for the total count and one for the first 100 entries
374
+ return RSVP.hash({
375
+ count: this.queryCount(),
376
+ entries: this.queryPage(1)
377
+ }).then(({count}) => {
378
+ // entries already set as part of queryPage
379
+ this.setProperties({
380
+ count,
381
+ searchingTable: false
382
+ });
383
+ }).catch(() => {
384
+ // request failed, clean up data
385
+ this.setProperties({
386
+ // search data
387
+ pageEntries: null,
388
+ count: 0,
389
+ // searching keys
390
+ searchingTable: false
391
+ });
392
+ });
393
+ },
394
+
395
+ /**
396
+ * Called when the export button is clicked to export all data
397
+ * @return {Promise} Promise that resolves after exporting the table
398
+ */
399
+ export() {
400
+ // TODO: canSearch?
401
+
402
+ // build table for export
403
+ let table = new Table(this.getExportColumns());
404
+ return this.queryAll().then((entries) => {
405
+ table.setRows(entries.sortBy(this.get('defaultSortKey')).reverse());
406
+ exportTable(table, `${this.getTitle()} - All Entries`);
407
+ }).catch(handleAjaxError.bind(this));
408
+ },
409
+
410
+ /* Pagination */
411
+
412
+ /**
413
+ * Called when a page button is clicked to switch pages
414
+ * @param {Number} page New page number to set
415
+ */
416
+ setPage(page) {
417
+ // clamp page number
418
+ let max = this.get('totalPages');
419
+ if (page < 1) {
420
+ page = 1;
421
+ } else if (page > max) {
422
+ page = max;
423
+ }
424
+
425
+ // if we havve entries at the page number, use those
426
+ // if missing, query them
427
+ if (isNone(this.get('pageEntries').objectAt(page - 1))) {
428
+ this.incrementProperty('pagesSearching');
429
+ this.set('page', page);
430
+ this.queryPage(page).then(() => {
431
+ // entries set in promise logic
432
+ this.decrementProperty('pagesSearching');
433
+ });
434
+ } else {
435
+ this.set('page', page);
436
+ }
437
+ },
438
+
439
+ /* Sorting */
440
+
441
+ /**
442
+ * Resorts the entry by the given column
443
+ * @param {Column} column Column to use for sorting
444
+ * @param {String} sortKey String to use for sorting in the column
445
+ * @return {Promise} Promise that resolves to entries
446
+ */
447
+ sortColumn(column, sortKey) {
448
+ // if the sort key is unchanged, do nothing
449
+ if (sortKey === this.get('tableSortKey')) {
450
+ return RSVP.resolve();
451
+ }
452
+
453
+ // mark that we are sorting, column properties do not add desc
454
+ column.set('sorting', true);
455
+
456
+ // search for data
457
+ let page = this.get('page');
458
+ return this.querySort(page, column.searchKey || column.valuePath, column.ascending).then((entries) => {
459
+ // set entries to new list
460
+ let pageEntries = [];
461
+ pageEntries[page - 1] = entries;
462
+ this.setProperties({pageEntries, currentSortKey: sortKey});
463
+
464
+ // mark that we are done sorting
465
+ column.set('sorting', false);
466
+ return entries;
467
+ });
468
+ }
469
+ }
470
+ });