1# Table 2 3All tables in the application are using the [BoostrapVue table component](https://bootstrap-vue.org/docs/components/table). 4 5To use the component, include the `<b-table>` tag in the template. The component is registered globally so does not need to be imported in each SFC. 6 7## Basic table 8There are a few required properties to maintain consistency across the application. The full list of options can be viewed on the [Bootstrap-vue table component's documentation page](https://bootstrap-vue.org/docs/components/table#comp-ref-b-table-props). 9 10 11### Required properties 12 13- `items` - renders table items 14- `fields` - renders table header 15- `hover` - enables table row hover state 16- `responsive` or `stacked` - makes the table responsive (enables horizontal scrolling or stacked view) at the defined breakpoint 17- `show-empty` *(required if table data is generated dynamically)* - shows an empty message if there are no items in the table 18- `empty-text` *(required if table data is generated dynamically)* - the translated empty message 19 20![Basic table example](./table.png) 21![Basic empty table example](./table-empty.png) 22 23```vue 24<template> 25 <b-table 26 hover 27 show-empty 28 responsive="md" 29 :items="items" 30 :fields="fields" 31 :empty-text="$t('global.table.emptyMessage')" 32 /> 33</template> 34 35<script> 36 export default { 37 data() { 38 items: [ 39 { 40 name: 'Babe', 41 age: '3 years', 42 color: 'white, orange, grey' 43 }, 44 { 45 name: 'Grey Boy', 46 age: '4 months', 47 color: 'grey' 48 }, 49 ], 50 fields: [ 51 { 52 key: 'name', 53 label: this.$t('table.name') //translated label 54 }, 55 { 56 key: 'age', 57 label: this.$t('table.age') //translated label 58 }, 59 { 60 key: 'color', 61 label: this.$t('table.color') // translated label 62 } 63 ] 64 } 65 } 66</script> 67``` 68 69## Sort 70 71To enable table sort, include `sortable: true` in the fields array for sortable columns and add the following props to the `<b-table>` component: 72 73- `sort-by` 74- `no-sort-reset` 75- `sort-icon-left` 76 77![Table sort example](./table-sort.png) 78 79 80```vue 81<template> 82 <b-table 83 hover 84 no-sort-reset 85 sort-icon-left 86 sort-by="rank" 87 responsive="md" 88 :items="items" 89 :fields="fields" 90 /> 91</template> 92<script> 93export default { 94 data() { 95 return { 96 items: [...], 97 fields: [ 98 { 99 key: 'name', 100 label: 'Name', //should be translated 101 sortable: true 102 }, 103 { 104 key: 'rank', 105 label: 'Rank', //should be translated 106 sortable: true 107 }, 108 { 109 key: 'description', 110 label: 'Description', //should be translated 111 sortable: false 112 } 113 ] 114 } 115 } 116} 117</script> 118``` 119 120## Expandable rows 121 122To add an expandable row in the table, add a column for the expand button in the fields array. Include the tdClass `table-row-expand` to ensure icon rotation is handled. Use the built in [cell slot](https://bootstrap-vue.org/docs/components/table#comp-ref-b-table-slots) to target the expand button column and add a button with the chevron icon. 123 124Include the [TableRowExpandMixin](https://github.com/openbmc/webui-vue/blob/master/src/components/Mixins/TableRowExpandMixin.js). The mixin contains the dynamic `aria-label` and `title` attribute values that need to be included with the expand button. The `toggleRowDetails` method should be the button's click event callback. Be sure to pass the `row` object to the function. 125 126Use the [row-details slot](https://bootstrap-vue.org/docs/components/table#comp-ref-b-table-slots) to format the expanded row content. The slot has access to the row `item` property. 127 128### Summary 129 1301. Add a column for the expansion row button with the tdClass, `table-row-expand` 1312. Include the `TableRowExpandMixin` to handle the dynamic aria label, title, and row expansion toggling 1323. Use the `#cell` slot to target the expandable row column and add the button with accessible markup and click handler 1334. Use the `#row-details` slot to format expanded row content 134 135![Table row expand example](./table-expand-row.png) 136 137```vue 138<template> 139 <b-table 140 hover 141 responsive="md" 142 :items="items" 143 :fields="fields" 144 > 145 <template #cell(expandRow)="row"> 146 <b-button 147 variant="link" 148 :aria-label="expandRowLabel" 149 :title="expandRowLabel" 150 @click="toggleRowDetails(row)" 151 > 152 <icon-chevron /> 153 </b-button> 154 </template> 155 <template #row-details="row"> 156 <h3>Expanded row details</h3> 157 {{ row.item }} 158 </template> 159 </b-table> 160</template> 161<script> 162import IconChevron from '@carbon/icons-vue/es/chevron--down/20'; 163import TableRowExpandMixin, { expandRowLabel } from '@/components/Mixins/TableRowExpandMixin'; 164 165export default { 166 components: { IconChevron }, 167 mixins: [ TableRowExpandMixin ], 168 data() { 169 return { 170 items: [...], 171 fields: [ 172 { 173 key: 'expandRow', 174 label: '', 175 tdClass: 'table-row-expand', 176 }, 177 ... 178 ], 179 expandRowLabel 180 } 181 } 182} 183</script> 184``` 185 186## Search 187 188The table is leveraging [BootstrapVue table filtering](https://bootstrap-vue.org/docs/components/table#filtering) for search. Add the [@filtered](https://bootstrap-vue.org/docs/components/table#filter-events) event listener onto the `<b-table>` component. The event callback should track the total filtered items count. 189 190Import the `<search>` and `<table-cell-count>` components and include them in the template above the `<b-table>` component. 191 192Include the [SearchFilterMixin](https://github.com/openbmc/webui-vue/blob/master/src/components/Mixins/SearchFilterMixin.js). Add the `@change-search` and `@clear-search` event listeners on the `<search>` component and use the corresponding `onChangeSearchInput` and `onClearSearchInput` methods as the event callbacks. The table should also include the dynamic `:filter` prop with `searchFilter` set as the value. 193 194The `<table-cell-count>` component requires two properties, total table item count and total filtered items count. 195 196Add the `:empty-filtered-text` prop to the table to show the translated message if there are no search matches. 197 198![Table search example](./table-search.png) 199 200![Table search active example](./table-search-active.png) 201 202![Table search empty example](./table-search-empty.png) 203 204```vue 205<template> 206 <b-container> 207 <b-row> 208 <b-col> 209 <search 210 @changeSearch="onChangeSearchInput" 211 @clearSearch="onClearSearchInput" 212 /> 213 </b-col> 214 <b-col> 215 <table-cell-count 216 :filtered-items-count="filteredItemsCount" 217 :total-number-of-cells="items.length" 218 /> 219 </b-col> 220 </b-row> 221 <b-table 222 hover 223 responsive="md" 224 :items="items" 225 :fields="fields" 226 :filter="searchFilter" 227 :empty-filtered-text="$t('global.table.emptySearchMessage')" 228 @filtered="onFiltered" 229 /> 230 </b-container> 231</template> 232<script> 233import Search from '@/components/Global/Search'; 234import TableCellCount from '@/components/Global/TableCellCount'; 235import SearchFilterMixin, { searchFilter } from '@/components/Mixins/SearchFilterMixin'; 236 237export default { 238 components: { Search, TableCellCount }, 239 mixins: [ SearchFilterMixin ], 240 data() { 241 return { 242 items: [...], 243 fields: [...], 244 searchFilter, 245 filteredItems: [], 246 } 247 }, 248 computed: { 249 filteredItemsCount() { 250 return this.filteredItems.length; 251 }, 252 }, 253 methods: { 254 onFiltered(items) { 255 this.filteredItems = items; 256 }, 257 }, 258} 259</script> 260``` 261 262## Row actions 263 264To add table row actions, add a column for the action buttons in the table. Then in the array of table items, add a corresponding array of actions for each item. The array should have each desired row action with a `value` and `title` property. 265 266Import the `<table-row-action>` component. Provide the `value` and `title` props to the component and use the named `#icons` slot to include an icon. The component will emit a `@click-table-action` with the event value. 267 268![Table row actions example](./table-row-actions.png) 269 270```vue 271<template> 272 <b-table 273 hover 274 responsive="md" 275 :items="itemsWithActions" 276 :fields="fields" 277 > 278 <template #cell(actions)="row"> 279 <table-row-action 280 v-for="(action, index) in row.item.actions" 281 :key="index" 282 :value="action.value" 283 :title="action.title" 284 @click-table-action="onTableRowAction($event, row.item)" 285 /> 286 <template #icon> 287 <icon-edit v-if="action.value === 'edit'"/> 288 <icon-delete v-if="action.value === 'delete'"/> 289 </template> 290 </table-row-action> 291 </template> 292 </b-table> 293</template> 294<script> 295import IconDelete from '@carbon/icons-vue/es/trash-can/20'; 296import IconEdit from '@carbon/icons-vue/es/edit/20'; 297import TableRowAction from '@/components/Global/TableRowAction'; 298 299export default { 300 components: { IconDelete, IconEdit, TableRowAction }, 301 data() { 302 return { 303 items: [...], 304 fields: [ 305 ..., 306 { 307 key: 'actions', 308 label: '', 309 tdClass: 'text-right text-nowrap', 310 } 311 ], 312 } 313 }, 314 computed: { 315 itemsWithActions() { 316 return this.items.map((item) => { 317 return { 318 ...item, 319 actions: [ 320 { 321 value: 'edit', 322 title: this.$t('global.action.edit'), 323 }, 324 { 325 value: 'delete', 326 title: this.$t('global.action.delete'), 327 }, 328 ], 329 }; 330 }); 331 } 332 }, 333 methods: { 334 onTableRowAction(event, row) { 335 // row action callback 336 } 337 } 338} 339</script> 340``` 341 342## Filters 343 344To add a table dropdown filter: 3451. Import the `<table-filter> `component and TableFilterMixin. 3461. Add a filters prop to the `<table-filters>` component. This prop should be an array of filter groups–each required to have a key, label, and values prop. 347 348The `label` prop value should be the translated filter group label. The `key` prop will usually match the filtered by table column key. The `values` prop should be an array of filter values that will render as a list of checkbox items in the dropdown. 349 350The component will emit a `@filter-change` event that will provide the filter group and all selected values in the group. Use the getFilteredTableData method from the TableFilterMixin to show the filtered table data. 351 352![Table filter example](./table-filter.png) 353 354![Table filter active example](./table-filter-active.png) 355 356```vue 357<template> 358 <b-container> 359 <b-row> 360 <b-col class="text-right"> 361 <table-filter 362 :filters="tableFilters" 363 @filter-change="onTableFilterChange" 364 /> 365 </b-col> 366 </b-row> 367 <b-table 368 hover 369 responsive="md" 370 :items="filteredItems" 371 :fields="fields" 372 /> 373 </b-container> 374</template> 375<script> 376import TableFilter from '@/components/Global/TableFilter'; 377import TableFilterMixin from '@/components/Mixins/TableFilterMixin'; 378 379export default { 380 components: { TableFilter }, 381 mixins: [ TableFilterMixin ], 382 data() { 383 return { 384 items: [...], 385 fields: [...], 386 tableFilters: [ 387 { 388 label: this.$t('table.status'), 389 key: status, 390 values: ['Open', 'Closed'] 391 } 392 ], 393 activeFilters: [], 394 }, 395 }, 396 computed: { 397 filteredItems() { 398 return this.getFilteredTableData(this.items, this.activeFilters); 399 }, 400 }, 401 methods: { 402 onTableFilterChange({ activeFilters }) { 403 this.activeFilters = activeFilters; 404 }, 405 }, 406} 407</script> 408``` 409 410 411### Date filter 412 413To add a date filter, import the `<table-date-filter>` component. It will emit a `@change` event with the user input date values. There is a date filter method, `getFilteredTableDataByDate`, in the `TableFilterMixin`. 414 415 416## Batch actions 417 418Batch actions allow a user to take a single action on many items in a table at once. 419 420To add table batch actions: 4211. Import the `<table-toolbar> `component and BVTableSelectableMixin 4221. Add the `selectable`, `no-select-on-click` props and a unique `ref` to the table. The table will emit a `@row-selected` event. Use the `onRowSelected` mixin method as a callback and provide the `$event` as the first argument and the total table items count as the second argument. 4231. Add a table column for checkboxes. The table header checkbox should use the `tableHeaderCheckboxModel` and `tableHeaderCheckboxIndeterminate` values provided by the mixin. The table header checkbox should also use the `onChangeHeaderCheckbox` method as a callback for the `@change` event with the table `ref` passed as an argument. The table row checkboxes should use the `toggleSelectRow` method as a callback for the `@change` event with the table `ref` passed as the first argument and the row index passed as the second argument. 4241. Add an actions prop to the `<table-toolbar>` component. This prop should be an array of toolbar actions–required to have a value and label prop. Add the `selected-items-count` prop to the `<table-toolbar>` component. The component will emit a `@batch-action` event that will provide the user selected action. It will also emit a `@clear-selected` event. Provide the `clearSelectedRows` as a callback with the table `ref` passed as an argument. 425 426![Table batch action example](./table-batch-action.png) 427 428![Table batch action active example](./table-batch-action-active.png) 429 430```vue 431<template> 432 <b-container> 433 <table-toolbar 434 :selected-items-count="selectedRows.length" 435 :actions="tableToolbarActions" 436 @clear-selected="clearSelectedRows($refs.table)" 437 @batch-action="onBatchAction" 438 /> 439 <b-table 440 ref="table" 441 hover 442 selectable 443 no-select-on-click 444 responsive="md" 445 :items="filteredItems" 446 :fields="fields" 447 @row-selected="onRowSelected($event, items.length)" 448 > 449 <template #head(checkbox)> 450 <b-form-checkbox 451 v-model="tableHeaderCheckboxModel" 452 :indeterminate="tableHeaderCheckboxIndeterminate" 453 @change="onChangeHeaderCheckbox($refs.table)" 454 /> 455 </template> 456 <template #cell(checkbox)="row"> 457 <b-form-checkbox 458 v-model="row.rowSelected" 459 @change="toggleSelectRow($refs.table, row.index)" 460 /> 461 </template> 462 </b-table> 463 </b-container> 464</template> 465<script> 466import TableToolbar from '@/components/Global/TableToolbar'; 467import BVTableSelectableMixin, { 468 tableHeaderCheckboxModel, 469 tableHeaderCheckboxIndeterminate, 470 selectedRows 471} from '@/components/Mixins/BVTableSelectableMixin'; 472 473export default { 474 components: { TableToolbar }, 475 mixins: [ BVTableSelectableMixin ], 476 data() { 477 return { 478 items: [...], 479 fields: [ 480 { 481 key: 'checkbox' 482 }, 483 ... 484 ], 485 tableToolbarActions: [ 486 { 487 value: 'edit', 488 label: this.$t('global.action.edit') 489 }, 490 { 491 value: 'delete', 492 label: this.$t('global.action.delete') 493 }, 494 ], 495 tableHeaderCheckboxModel, 496 tableHeaderCheckboxIndeterminate, 497 selectedRows 498 }, 499 }, 500 methods: { 501 onBatchAction(action) { 502 // Do something with selected batch action and selected rows 503 }, 504 }, 505} 506</script> 507``` 508 509 510## Pagination 511 512To add table pagination: 5131. Import the BVPaginationMixin 5141. Add the `per-page` and `current-page` props to the `<table>` component. 5151. Add the below HTML snippet to the template. Make sure to update the `total-rows` prop. 516 517```vue{21} 518<b-row> 519 <b-col sm="6"> 520 <b-form-group 521 class="table-pagination-select" 522 :label="$t('global.table.itemsPerPage')" 523 label-for="pagination-items-per-page" 524 > 525 <b-form-select 526 id="pagination-items-per-page" 527 v-model="perPage" 528 :options="itemsPerPageOptions" 529 /> 530 </b-form-group> 531 </b-col> 532 <b-col sm="6"> 533 <b-pagination 534 v-model="currentPage" 535 first-number 536 last-number 537 :per-page="perPage" 538 :total-rows="getTotalRowCount(items.length)" 539 aria-controls="table-event-logs" 540 /> 541 </b-col> 542</b-row> 543``` 544![Table pagination example](./table-pagination.png) 545 546```vue 547<template> 548 <b-container> 549 <b-table 550 hover 551 responsive="md" 552 :items="filteredItems" 553 :fields="fields" 554 :per-page="perPage" 555 :current-page="currentPage" 556 /> 557 <b-row> 558 <b-col sm="6"> 559 <b-form-group 560 class="table-pagination-select" 561 :label="$t('global.table.itemsPerPage')" 562 label-for="pagination-items-per-page" 563 > 564 <b-form-select 565 id="pagination-items-per-page" 566 v-model="perPage" 567 :options="itemsPerPageOptions" 568 /> 569 </b-form-group> 570 </b-col> 571 <b-col sm="6"> 572 <b-pagination 573 v-model="currentPage" 574 first-number 575 last-number 576 :per-page="perPage" 577 :total-rows="getTotalRowCount(items.length)" 578 aria-controls="table-event-logs" 579 /> 580 </b-col> 581 </b-row> 582 </b-container> 583</template> 584<script> 585import BVPaginationMixin, { 586 currentPage, 587 perPage, 588 itemsPerPageOptions 589} from '@/components/Mixins/BVPaginationMixin'; 590 591export default { 592 mixins: [ BVPaginationMixin ], 593 data() { 594 return { 595 items: [...], 596 fields: [..], 597 currentPage, 598 perPage, 599 itemsPerPageOptions 600 }, 601 } 602} 603</script> 604```