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)
635 lines (524 loc) • 17.7 kB
JavaScript
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 (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())
this.$header.add(this.$header_).find('span.order').remove()
if (this.options.sortName === $this.data('field')) {
const currentSortOrder = this.options.sortOrder
const initialSortOrder = this.columns[this.fieldsColumnsIndex[$this.data('field')]].sortOrder ||
this.columns[this.fieldsColumnsIndex[$this.data('field')]].order
if (currentSortOrder === undefined) {
this.options.sortOrder = 'asc'
} else if (currentSortOrder === 'asc') {
this.options.sortOrder = this.options.sortReset ? initialSortOrder === 'asc' ? 'desc' : undefined : 'desc'
} else if (this.options.sortOrder === 'desc') {
this.options.sortOrder = this.options.sortReset ? initialSortOrder === 'desc' ? 'asc' : undefined : 'asc'
}
if (this.options.sortOrder === undefined) {
this.options.sortName = undefined
}
} else {
this.options.sortName = $this.data('field')
if (this.options.rememberOrder) {
this.options.sortOrder = $this.data('order') === 'asc' ? 'desc' : 'asc'
} else {
this.options.sortOrder = this.columns[this.fieldsColumnsIndex[$this.data('field')]].sortOrder ||
this.columns[this.fieldsColumnsIndex[$this.data('field')]].order
}
}
$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 fixedScroll = false
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
}
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)
}
}