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