Completed
Push — master ( 97eddd...5cf35a )
by Jonathan
19:50 queued 09:35
created

datatable.js ➔ getDatatableConfig   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 18
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 12
nc 2
nop 1
dl 0
loc 18
rs 9.8
c 0
b 0
f 0
1
import 'datatables.net-bs';
2
import 'datatables.net-buttons-bs';
3
import 'datatables.net-buttons/js/buttons.colVis';
4
// import 'datatables.net-colreorder';
5
import 'datatables.net-fixedcolumns';
6
import 'datatables.net-responsive-bs';
7
import 'datatables.net-select';
8
import { sprintf } from 'sprintf-js'
9
import { Link } from './link'
10
11
export class Datatable {
12
    /**
13
     * Init Datatable configuration
14
     * @param {Element} element
15
     */
16
    async init(element) {
17
        let linkManager = new Link(false)
18
19
        $(element).hide()
20
21
        await this.getDatatableConfig(element)
22
23
        this.table = $(element).DataTable({
24
            dom: 'Brtp',
25
            autoWidth: false, // Else the width is not refreshed on window resize
26
            responsive: true,
27
            colReorder: false,
28
            serverSide: true,
29
            ajax: {
30
                url: this.url,
31
                type: "POST"
32
            },
33
            pageLength: this.getInitialPageLength(),
34
            order: this.getInitialOrder(),
35
            columnDefs: this.getDatatableColumnDefs(element),
36
            createdRow: (row, data, index) => {
0 ignored issues
show
Unused Code introduced by
The parameter index is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
37
                // Go to detail view when you click on a row
38
                $('td:gt(0):lt(-1)', row).click(() => {
39
                    document.location.href = sprintf(this.rowUrl, data.id);
40
                })
41
42
                // Init click listener on delete button
43
                linkManager.initClickListener(row)
44
45
                // Init click listener on the row if a callback is defined
46
                if (typeof this.rowClickCallback !== 'undefined') {
47
                    $(row).on('click', (event) => {
48
                        this.rowClickCallback(event, this.table, data)
49
                    })
50
                }
51
            },
52
            buttons: [
53
                {
54
                    extend: 'colvis',
55
                    columns: ':gt(0):lt(-1)',
56
                    columnText: ( dt, idx, title ) => {
0 ignored issues
show
Unused Code introduced by
The parameter title is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
57
                        let column = this.columns[idx-1]
58
                        return $(`th[data-name="${column.name}"]`).data('label')
59
                    }
60
                }
61
            ],
62
            language: {
63
                paginate: {
64
                    previous: '<',
65
                    next: '>'
66
                },
67
                buttons: {
68
                    colvis: uctrans('button.columns')
69
                }
70
            },
71
            aoSearchCols: this.getInitialSearch(),
72
73
        });
74
75
        // Config buttons
76
        this.configButtons()
77
78
        // Init search
79
        this.initDatatableColumnSearch()
80
81
        // Hidde loader
82
        $(element).parents('.table-responsive:first').find('.loader').hide()
83
84
        // Show datatable
85
        $(element).show()
86
    }
87
88
    /**
89
     * Async method to get Datatable configuration: columns and selected filter definition.
90
     * @param {Element} element
91
     */
92
    async getDatatableConfig(element) {
93
        let filterId = $(element).data('filter-id')
94
        if (typeof filterId === 'undefined') {
95
            filterId = ''
96
        }
97
98
        let url = laroute.route('uccello.datatable.config', {
0 ignored issues
show
Bug introduced by
The variable laroute seems to be never declared. If this is a global, consider adding a /** global: laroute */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
99
            domain: this.domainSlug,
100
            module: this.moduleName,
101
            filter: filterId,
102
            filter_type: $(element).data('filter-type')
103
        })
104
105
        let response = await $.get(url)
106
107
        this.columns = response.columns
108
        this.selectedFilter = response.filter
109
    }
110
111
    /**
112
     * Make datatable columns from filter.
113
     * @param {Element} element
114
     * @return {array}
115
     */
116
    getDatatableColumnDefs(element) {
117
        let selector = new UccelloUitypeSelector.UitypeSelector() // UccelloUitypeSelector is replaced automaticaly by webpack. See webpack.mix.js
0 ignored issues
show
Bug introduced by
The variable UccelloUitypeSelector seems to be never declared. If this is a global, consider adding a /** global: UccelloUitypeSelector */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
118
119
        let datatableColumns = [];
120
121
        // Add first column
122
        datatableColumns.push({
123
            targets: 0,
124
            data: null,
125
            defaultContent: '',
126
            orderable: false,
127
            searchable: false
128
        })
129
130
        // Add all filter columns
131
        for (let i in this.columns) {
132
            let column = this.columns[i]
133
            datatableColumns.push({
134
                targets: parseInt(i) + 1, // Force integer
135
                data: column.name,
136
                orderable: true,
137
                createdCell: (td, cellData, rowData, row, col) => {
138
                    selector.get(column.uitype).createdCell(column, td, cellData, rowData, row, col)
139
                },
140
                visible: column.visible
141
            })
142
        }
143
144
        // Add last column (action buttons)
145
        datatableColumns.push({
146
            targets: this.columns.length + 1,
147
            data: null,
148
            defaultContent: '',
149
            orderable: false,
150
            searchable: false,
151
            createdCell: this.getActionsColumnCreatedCell(element)
152
        })
153
154
        return datatableColumns;
155
    }
156
157
    /**
158
     * Initialize initial columns search, according to selected filter conditions
159
     * @return {array}
160
     */
161
    getInitialSearch() {
162
        let search = []
163
164
        if (this.selectedFilter && this.selectedFilter.conditions && this.selectedFilter.conditions.search) {
165
            // First column
166
            search.push(null)
167
168
            for (let i in this.columns) {
169
                let value = null
0 ignored issues
show
Unused Code introduced by
The assignment to value seems to be never used. If you intend to free memory here, this is not necessary since the variable leaves the scope anyway.
Loading history...
170
171
                let column =  this.columns[i]
172
                value = typeof this.selectedFilter.conditions.search[column.name] !== 'undefined' ? this.selectedFilter.conditions.search[column.name] : null
173
174
                if (value) {
175
                    search.push({sSearch: value})
176
                } else {
177
                    search.push(null)
178
                }
179
            }
180
181
            // Last column
182
            search.push(null)
183
        }
184
185
        return search
186
    }
187
188
    /**
189
     * Get initial order
190
     * @return {array}
191
     */
192
    getInitialOrder() {
193
        let order = [[1, 'asc']] // Default
194
        if (this.selectedFilter && this.selectedFilter.data && this.selectedFilter.data.order) {
195
            order = this.selectedFilter.data.order
196
        }
197
198
        return order
199
    }
200
201
    /**
202
     * Get initial page length
203
     * @return {integer}
204
     */
205
    getInitialPageLength() {
206
        let length = 15 // Default
207
        if (this.selectedFilter && this.selectedFilter.data && this.selectedFilter.data.length) {
208
            length = this.selectedFilter.data.length
209
        }
210
211
        return length
212
    }
213
214
    /**
215
     * Make datatable action column.
216
     * @param {Element} element
217
     */
218
    getActionsColumnCreatedCell(element) {
219
        return (td, cellData, rowData, row, col) => {
0 ignored issues
show
Unused Code introduced by
The parameter col is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
Unused Code introduced by
The parameter row is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
220
            var dataTableContainer = $(element).parents('.dataTable-container:first');
221
222
            // Copy buttons from template
223
            let editButton = $(".template .edit-btn", dataTableContainer).clone().tooltip().appendTo($(td))
224
            let deleteButton = $(".template .delete-btn", dataTableContainer).clone().tooltip().appendTo($(td))
225
226
            // Config edit link url
227
            if (editButton.attr('href')) {
228
                let editLink = editButton.attr('href').replace('RECORD_ID', rowData.id)
229
                editButton.attr('href', editLink)
230
            }
231
232
            // Config delete link url
233
            if (deleteButton.attr('href')) {
234
                let deleteLink = deleteButton.attr('href').replace('RECORD_ID', rowData.id)
235
236
                // if relation_id is defined, replace RELATION_ID
237
                if (rowData.relation_id) {
238
                    deleteLink = deleteLink.replace('RELATION_ID', rowData.relation_id)
239
                }
240
241
                deleteButton.attr('href', deleteLink)
242
            }
243
        }
244
    }
245
246
    /**
247
     * Config buttons to display them correctly
248
     */
249
    configButtons() {
250
        let table = this.table
251
252
        // Get buttons container
253
        let buttonsContainer = table.buttons().container()
254
255
        // Retrieve container
256
        let dataTableContainer = buttonsContainer.parents('.dataTable-container:first');
257
258
        // Remove old buttons if datatable was initialized before (e.g. in related list selection modal)
259
        $('.action-buttons .buttons-colvis', dataTableContainer).remove()
260
261
        // Display mini buttons (related lists)
262
        if (dataTableContainer.data('button-size') === 'mini') {
263
264
            $('button', buttonsContainer).each((index, element) => {
265
                // Get content and use it as a title
266
                let title = $('span', element).html()
267
268
                // Add icon and effect
269
                $(element)
270
                    .addClass('btn-circle waves-effect waves-circle waves-float bg-primary')
271
                    .removeClass('btn-default')
272
                    .html('<i class="material-icons">view_column</i>')
273
                    .attr('title', title)
274
                    .tooltip({
275
                        placement: 'top'
276
                    })
277
278
                // Move button
279
                $(element).prependTo($('.action-buttons', dataTableContainer))
280
            })
281
        }
282
        // Display classic buttons (list)
283
        else {
284
            // Move buttons
285
            buttonsContainer.appendTo($('.action-buttons', dataTableContainer));
286
287
            $('button', buttonsContainer).each((index, element) => {
288
                // Replace <span>...</span> by its content
289
                $(element).html($('span', element).html())
290
291
                // Add icon and effect
292
                $(element).addClass('icon-right waves-effect bg-primary')
293
                $(element).removeClass('btn-default')
294
                $(element).append('<i class="material-icons">keyboard_arrow_down</i>')
295
            })
296
297
            // Move to the right
298
            $('.action-buttons .btn-group', dataTableContainer).addClass('pull-right')
299
        }
300
301
        // Change records number
302
        $('ul#items-number a').on('click', (event) => {
303
            let recordsNumber = $(event.target).data('number')
304
            $('strong.records-number').text(recordsNumber)
305
            table.page.len(recordsNumber).draw()
306
        })
307
308
        $(".dataTables_paginate", dataTableContainer).appendTo($('.paginator', dataTableContainer))
309
    }
310
311
    /**
312
     * Config column search.
313
     */
314
    initDatatableColumnSearch()
315
    {
316
        let table = this.table
317
        this.timer = 0
318
        let that = this
319
320
        // Config each column
321
        table.columns().every(function (index) {
322
            let column = table.column(index)
323
324
            // Event listener to launch search
325
            $('input', this.header()).on('keyup apply.daterangepicker cancel.daterangepicker', function() {
326
                that.launchSearch(column, $(this).val())
327
            })
328
329
            $('select', this.header()).on('change', function() {
330
                that.launchSearch(column, $(this).val())
331
            })
332
        })
333
334
        // Add clear search button listener
335
        this.addClearSearchButtonListener()
336
    }
337
338
    /**
339
     * Launch search
340
     * @param {Object} column
341
     * @param {String} q
342
     */
343
    launchSearch(column, q)
344
    {
345
        if (q !== '') {
346
            $('.clear-search').show()
347
        }
348
349
        if (column.search() !== q) {
350
            clearTimeout(this.timer)
351
            this.timer = setTimeout(() => {
352
                column.search(q)
353
                this.table.draw()
354
            }, 500)
355
        }
356
    }
357
358
    /**
359
     * Clear datatable search
360
     */
361
    addClearSearchButtonListener()
362
    {
363
        let table = this.table
364
365
        $('.actions-column .clear-search').on('click', (event) => {
366
            // Clear all search fields
367
            $('.dataTable thead select').selectpicker('deselectAll')
368
            $('.dataTable thead input').val('')
369
370
            // Update columns
371
            table.columns().every(function (index) {
372
                let column = table.column(index)
373
                column.search('')
374
            })
375
376
            // Disable clear search button
377
            $(event.currentTarget).hide()
378
379
            // Update data
380
            table.draw()
381
        })
382
    }
383
}