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 21 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 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 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 199 200 201 202 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 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 353 354 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 427 428 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 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```