UNPKG

bootstrap-table

Version:

An extended table to integration with some of the most widely used CSS frameworks. (Supports Bootstrap, Semantic UI, Bulma, Material Design, Foundation)

653 lines (538 loc) • 18.5 kB
import Utils from '../utils/index.js' export default { initServer (silent, query) { let data = {} const index = this.header.fields.indexOf(this.options.sortName) let params = { searchText: this.searchText, sortName: this.options.sortName, sortOrder: this.options.sortOrder } if (this.header.sortNames[index]) { params.sortName = this.header.sortNames[index] } if (this.options.pagination && this.options.sidePagination === 'server') { params.pageSize = this.options.pageSize === this.options.formatAllRows() ? this.options.totalRows : this.options.pageSize params.pageNumber = this.options.pageNumber } if (!this.options.url && !this.options.ajax) { return } if (this.options.queryParamsType === 'limit') { params = { search: params.searchText, sort: params.sortName, order: params.sortOrder } if (this.options.pagination && this.options.sidePagination === 'server') { params.offset = this.options.pageSize === this.options.formatAllRows() ? 0 : this.options.pageSize * (this.options.pageNumber - 1) params.limit = this.options.pageSize if (params.limit === 0 || this.options.pageSize === this.options.formatAllRows()) { delete params.limit } } } if ( this.options.search && this.options.sidePagination === 'server' && this.options.searchable && this.columns.filter(column => column.searchable).length ) { params.searchable = [] for (const column of this.columns) { if ( !column.checkbox && column.searchable && ( this.options.visibleSearch && column.visible || !this.options.visibleSearch ) ) { params.searchable.push(column.field) } } } if (!Utils.isEmptyObject(this.filterColumnsPartial)) { params.filter = JSON.stringify(this.filterColumnsPartial, null) } Utils.extend(params, query || {}) data = Utils.calculateObjectValue(this.options, this.options.queryParams, [params], data) // false to stop request if (data === false) { return } if (!silent) { this.showLoading() } const request = Utils.extend({}, Utils.calculateObjectValue(null, this.options.ajaxOptions), { type: this.options.method, url: this.options.url, data: this.options.contentType === 'application/json' && this.options.method === 'post' ? JSON.stringify(data) : data, cache: this.options.cache, contentType: this.options.contentType, dataType: this.options.dataType, success: (_res, textStatus, jqXHR) => { const res = Utils.calculateObjectValue(this.options, this.options.responseHandler, [_res, jqXHR], _res) if ( this.options.sidePagination === 'client' && this.options.paginationLoadMore ) { this._paginationLoaded = this.data.length === res.length } this.load(res) this.trigger('load-success', res, jqXHR && jqXHR.status, jqXHR) if (!silent) { this.hideLoading() } if ( this.options.sidePagination === 'server' && this.options.pageNumber > 1 && res[this.options.totalField] > 0 && !res[this.options.dataField].length ) { this.updatePagination() } }, error: jqXHR => { // abort ajax by multiple request if (jqXHR && jqXHR.status === 0 && this._xhrAbort) { this._xhrAbort = false return } let data = [] if (this.options.sidePagination === 'server') { data = {} data[this.options.totalField] = 0 data[this.options.dataField] = [] } this.load(data) this.trigger('load-error', jqXHR && jqXHR.status, jqXHR) if (!silent) { this.hideLoading() } } }) if (this.options.ajax) { Utils.calculateObjectValue(this, this.options.ajax, [request], null) } else { if (this._xhr && this._xhr.readyState !== 4) { this._xhrAbort = true this._xhr.abort() } this._xhr = $.ajax(request) } return data }, initData (data, type) { if (type === 'append') { this.options.data = this.options.data.concat(data) } else if (type === 'prepend') { this.options.data = [].concat(data).concat(this.options.data) } else { data = data || Utils.deepCopy(this.options.data) this.options.data = Array.isArray(data) ? data : data[this.options.dataField] } this.data = [...this.options.data] if (this.options.sortReset) { this.unsortedData = [...this.data] } if (this.options.sidePagination === 'server') { return } this.initSort() }, initSort () { let name = this.options.sortName const order = this.options.sortOrder === 'desc' ? -1 : 1 const index = this.header.fields.indexOf(this.options.sortName) if (index !== -1) { if (this.options.sortStable) { this.data.forEach((row, i) => { if (!row.hasOwnProperty('_position')) { row._position = i } }) } if (Utils.hasRowspanCells(this.data)) { Utils.flattenRowspanCells(this.data) } if (this.options.customSort) { Utils.calculateObjectValue(this.options, this.options.customSort, [ this.options.sortName, this.options.sortOrder, this.data ]) } else { this.data.sort((a, b) => { if (this.header.sortNames[index]) { name = this.header.sortNames[index] } const aa = Utils.getItemField(a, name, this.options.escape) const bb = Utils.getItemField(b, name, this.options.escape) const value = Utils.calculateObjectValue(this.header, this.header.sorters[index], [aa, bb, a, b]) if (value !== undefined) { if (this.options.sortStable && value === 0) { return order * (a._position - b._position) } return order * value } return Utils.sort(aa, bb, order, this.options, a._position, b._position) }) } if (this.options.sortClass !== undefined) { setTimeout(() => { this.$el.removeClass(this.options.sortClass) const index = this.$header.find(`[data-field="${this.options.sortName}"]`).index() this.$el.find(`tr td:nth-child(${index + 1})`).addClass(this.options.sortClass) }, 250) } } else if (this.options.sortReset) { this.data = [...this.unsortedData] } }, onSort ({ type, currentTarget }) { const $this = type === 'keypress' ? $(currentTarget) : $(currentTarget).parent() const $this_ = this.$header.find('th').eq($this.index()) const column = this.columns[this.fieldsColumnsIndex[$this.data('field')]] this.$header.add(this.$header_).find('span.order').remove() // Resolve the effective sort-direction cycle for this column: // column orderList > global orderList > legacy [order, opposite(order)] // orderList values are normalized to arrays at init time; an unset/invalid // value stays undefined so the legacy column `order` fallback takes over. // The only deliberate change vs. the old cycle: order 'desc' with sortReset // previously got stuck (asc <-> undefined) and now cycles // desc -> asc -> undefined -> desc. let orderList = column.orderList || this.options.orderList if (!orderList) { const order = column.sortOrder || column.order || 'asc' orderList = [order, order === 'asc' ? 'desc' : 'asc'] } if (this.options.sortName === $this.data('field')) { // Same column clicked again: advance one step through the cycle. With // sortReset, a trailing "unsorted" state (handled inline to avoid an // array allocation) clears the sort entirely. const len = orderList.length const nextIndex = (orderList.indexOf(this.options.sortOrder) + 1) % (this.options.sortReset ? len + 1 : len) this.options.sortOrder = nextIndex === len ? undefined : orderList[nextIndex] if (this.options.sortOrder === undefined) { this.options.sortName = undefined } } else { // New column: start at the first direction, or — under rememberOrder — // advance one step from the column's last direction (legacy behavior, // now respecting a custom orderList too). this.options.sortName = $this.data('field') if (this.options.rememberOrder) { this.options.sortOrder = orderList[(orderList.indexOf($this.data('order')) + 1) % orderList.length] } else { this.options.sortOrder = orderList[0] } } $this.add($this_).data('order', this.options.sortOrder) // Assign the correct sortable arrow this.resetCaret() this._sort() }, _sort () { if (this.options.sidePagination === 'server' && this.options.serverSort) { this.options.pageNumber = 1 this.trigger('sort', this.options.sortName, this.options.sortOrder) this.initServer(this.options.silentSort) return } if (this.options.pagination && this.options.sortResetPage) { this.options.pageNumber = 1 this.initPagination() } this.trigger('sort', this.options.sortName, this.options.sortOrder) this.initSort() this.initBody() }, sortReset () { this.options.sortName = undefined this.options.sortOrder = undefined this._sort() }, sortBy (params) { this.options.sortName = params.field this.options.sortOrder = params.hasOwnProperty('sortOrder') ? params.sortOrder : 'asc' this._sort() }, getData (params) { let data = this.options.data if ( ( this.searchText || this.options.customSearch || this.options.sortName !== undefined || this.enableCustomSort || // Fix #4616: this.enableCustomSort is for extensions !Utils.isEmptyObject(this.filterColumns) || typeof this.options.filterOptions.filterAlgorithm === 'function' || !Utils.isEmptyObject(this.filterColumnsPartial) ) && (!params || !params.unfiltered) ) { data = this.data } if (params && !params.includeHiddenRows) { const hiddenRows = this.getHiddenRows() data = data.filter(row => Utils.findIndex(hiddenRows, row) === -1) } if (params && params.useCurrentPage) { data = data.slice(this.pageFrom - 1, this.pageTo) } if (params && params.formatted) { return data.map(row => { const formattedColumns = {} for (const [key, value] of Object.entries(row)) { const column = this.columns[this.fieldsColumnsIndex[key]] if (!column) { continue } formattedColumns[key] = Utils.calculateObjectValue(column, this.header.formatters[column.fieldIndex], [value, row, row.index, column.field], value) } return formattedColumns }) } return data }, getFooterData () { return this.footerData ?? [] }, load (_data) { let data = _data // #431: support pagination if (this.options.pagination && this.options.sidePagination === 'server') { this.options.totalRows = data[this.options.totalField] this.options.totalNotFiltered = data[this.options.totalNotFilteredField] this.footerData = data[this.options.footerField] ? [data[this.options.footerField]] : undefined } const fixedScroll = this.options.fixedScroll || data.fixedScroll data = Array.isArray(data) ? data : data[this.options.dataField] this.initData(data) this.initSearch() this.initPagination() this.initBody(fixedScroll) }, append (data) { this.initData(data, 'append') this.initSearch() this.initPagination() this.initSort() this.initBody(true) }, prepend (data) { this.initData(data, 'prepend') this.initSearch() this.initPagination() this.initSort() this.initBody(true) }, remove (params) { let removed = 0 for (let i = this.options.data.length - 1; i >= 0; i--) { const row = this.options.data[i] const value = Utils.getItemField(row, params.field, this.options.escape, row.escape) if (value === undefined && params.field !== '$index') { continue } if ( !row.hasOwnProperty(params.field) && params.field === '$index' && params.values.includes(i) || params.values.includes(value) ) { removed++ this.options.data.splice(i, 1) } } if (!removed) { return } if (this.options.sidePagination === 'server') { this.options.totalRows -= removed this.data = [...this.options.data] } this.initSearch() this.initPagination() this.initSort() this.initBody(true) }, removeAll () { if (this.options.data.length > 0) { this.data.splice(0, this.data.length) this.options.data.splice(0, this.options.data.length) this.initSearch() this.initPagination() this.initBody(true) } }, insertRow (params) { if (!params.hasOwnProperty('index') || !params.hasOwnProperty('row')) { return } const row = this.data[params.index] const originalIndex = this.options.data.indexOf(row) if (originalIndex === -1) { this.append([params.row]) return } this.data.splice(params.index, 0, params.row) this.options.data.splice(originalIndex, 0, params.row) this.initSearch() this.initPagination() this.initSort() this.initBody(true) }, updateRow (params) { const allParams = Array.isArray(params) ? params : [params] for (const params of allParams) { if (!params.hasOwnProperty('index') || !params.hasOwnProperty('row')) { continue } const row = this.data[params.index] const originalIndex = this.options.data.indexOf(row) if (params.hasOwnProperty('replace') && params.replace) { this.data[params.index] = params.row this.options.data[originalIndex] = params.row } else { Utils.extend(this.data[params.index], params.row) Utils.extend(this.options.data[originalIndex], params.row) } } this.initSearch() this.initPagination() this.initSort() this.initBody(true) }, getRowByUniqueId (_id) { const uniqueId = this.options.uniqueId const len = this.options.data.length let id = _id let dataRow = null let i let row for (i = len - 1; i >= 0; i--) { row = this.options.data[i] const rowUniqueId = Utils.getItemField(row, uniqueId, this.options.escape, row.escape) if (rowUniqueId === undefined) { continue } if (typeof rowUniqueId === 'string') { id = _id.toString() } else if (typeof rowUniqueId === 'number') { if (Number(rowUniqueId) === rowUniqueId && rowUniqueId % 1 === 0) { id = parseInt(_id, 10) } else if (rowUniqueId === Number(rowUniqueId) && rowUniqueId !== 0) { id = parseFloat(_id) } } if (rowUniqueId === id) { dataRow = row break } } return dataRow }, updateByUniqueId (params) { const allParams = Array.isArray(params) ? params : [params] let updatedUid = null for (const params of allParams) { if (!params.hasOwnProperty('id') || !params.hasOwnProperty('row')) { continue } const rowId = this.options.data.indexOf(this.getRowByUniqueId(params.id)) if (rowId === -1) { continue } if (params.hasOwnProperty('replace') && params.replace) { this.options.data[rowId] = params.row } else { Utils.extend(this.options.data[rowId], params.row) } updatedUid = params.id } this.initSearch() this.initPagination() this.initSort() this.initBody(true, updatedUid) }, removeByUniqueId (id) { const len = this.options.data.length const row = this.getRowByUniqueId(id) if (row) { this.options.data.splice(this.options.data.indexOf(row), 1) } if (len === this.options.data.length) { return } if (this.options.sidePagination === 'server') { this.options.totalRows -= 1 this.data = [...this.options.data] } this.initSearch() this.initPagination() this.initBody(true) }, _updateCellOnly (field, index) { if (index === -1) { return } const rowHtml = this.initRow(this.data[index], index) let fieldIndex = this.getVisibleFields().indexOf(field) if (fieldIndex === -1) { return } fieldIndex += Utils.getDetailViewIndexOffset(this.options) this.$body.find(`>tr[data-index=${index}]`) .find(`>td:eq(${fieldIndex})`) .replaceWith($(rowHtml).find(`>td:eq(${fieldIndex})`)) this.initBodyEvent() this.initFooter() this.resetView() this.updateSelected() }, updateCell (params) { if (!params.hasOwnProperty('index') || !params.hasOwnProperty('field') || !params.hasOwnProperty('value')) { return } const row = this.data[params.index] const originalIndex = this.options.data.indexOf(row) this.data[params.index][params.field] = params.value this.options.data[originalIndex][params.field] = params.value if (params.reinit === false) { this._updateCellOnly(params.field, params.index) return } this.initSort() this.initBody(true) }, updateCellByUniqueId (params) { const allParams = Array.isArray(params) ? params : [params] allParams.forEach(({ id, field, value }) => { const row = this.getRowByUniqueId(id) const index = this.data.indexOf(row) const originalIndex = this.options.data.indexOf(row) if (!row || index === -1) { return } this.data[index][field] = value this.options.data[originalIndex][field] = value }) if (params.reinit === false) { this._updateCellOnly(params.field, this.data.indexOf(this.getRowByUniqueId(params.id))) return } this.initSort() this.initBody(true) } }