diff --git a/src/components/table/helpers/mixin-tbody-row.js b/src/components/table/helpers/mixin-tbody-row.js index ac5200c6a92..2a7c77b96f4 100644 --- a/src/components/table/helpers/mixin-tbody-row.js +++ b/src/components/table/helpers/mixin-tbody-row.js @@ -205,12 +205,12 @@ export default { // rows index within the tbody. // See: https://github.com/bootstrap-vue/bootstrap-vue/issues/2410 const primaryKey = this.primaryKey - const hasPkValue = primaryKey && !isUndefinedOrNull(item[primaryKey]) - const rowKey = hasPkValue ? toString(item[primaryKey]) : String(rowIndex) + const primaryKeyValue = toString(get(item, primaryKey)) || null + const rowKey = primaryKeyValue || String(rowIndex) // If primary key is provided, use it to generate a unique ID on each tbody > tr // In the format of '{tableId}__row_{primaryKeyValue}' - const rowId = hasPkValue ? this.safeId(`_row_${item[primaryKey]}`) : null + const rowId = primaryKeyValue ? this.safeId(`_row_${primaryKeyValue}`) : null // Selectable classes and attributes const selectableClasses = this.selectableRowClasses ? this.selectableRowClasses(rowIndex) : {} @@ -233,8 +233,7 @@ export default { attrs: { id: rowId, tabindex: hasRowClickHandler ? '0' : null, - 'data-pk': rowId ? String(item[primaryKey]) : null, - // Should this be `aria-details` instead? + 'data-pk': primaryKeyValue || null, 'aria-details': detailsId, 'aria-owns': detailsId, 'aria-rowindex': ariaRowIndex, diff --git a/src/components/table/helpers/mixin-tbody.js b/src/components/table/helpers/mixin-tbody.js index 322ada1b189..ae27c17dc61 100644 --- a/src/components/table/helpers/mixin-tbody.js +++ b/src/components/table/helpers/mixin-tbody.js @@ -1,5 +1,5 @@ import KeyCodes from '../../../utils/key-codes' -import { arrayIncludes } from '../../../utils/array' +import { arrayIncludes, from as arrayFrom } from '../../../utils/array' import { closest, isElement } from '../../../utils/dom' import { props as tbodyProps, BTbody } from '../tbody' import filterEvent from './filter-event' @@ -23,11 +23,14 @@ export default { // Returns all the item TR elements (excludes detail and spacer rows) // `this.$refs.itemRows` is an array of item TR components/elements // Rows should all be B-TR components, but we map to TR elements + // Also note that `this.$refs.itemRows` may not always be in document order + const tbody = this.$refs.tbody.$el || this.$refs.tbody + const trs = (this.$refs.itemRows || []).map(tr => tr.$el || tr) // TODO: This may take time for tables many rows, so we may want to cache // the result of this during each render cycle on a non-reactive // property. We clear out the cache as each render starts, and // populate it on first access of this method if null - return (this.$refs.itemRows || []).map(tr => tr.$el || tr) + return arrayFrom(tbody.children).filter(tr => arrayIncludes(trs, tr)) }, getTbodyTrIndex(el) { // Returns index of a particular TBODY item TR diff --git a/src/components/table/tbody.js b/src/components/table/tbody.js index dded142de67..7b1033ead86 100644 --- a/src/components/table/tbody.js +++ b/src/components/table/tbody.js @@ -71,21 +71,25 @@ export const BTbody = /*#__PURE__*/ Vue.extend({ }, tbodyProps() { return this.tbodyTransitionProps ? { ...this.tbodyTransitionProps, tag: 'tbody' } : {} - }, - tbodyListeners() { - const handlers = this.tbodyTransitionHandlers || {} - return { ...this.$listeners, ...handlers } } }, render(h) { + const data = { + props: this.tbodyProps, + attrs: this.tbodyAttrs + } + if (this.isTransitionGroup) { + // We use native listeners if a transition group + // for any delegated events + data.on = this.tbodyTransitionHandlers || {} + data.nativeOn = this.$listeners || {} + } else { + // Otherwise we place any listeners on the tbody element + data.on = this.$listeners || {} + } return h( this.isTransitionGroup ? 'transition-group' : 'tbody', - { - props: this.tbodyProps, - attrs: this.tbodyAttrs, - // Pass down any listeners - on: this.tbodyListeners - }, + data, this.normalizeSlot('default', {}) ) }