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![Basic table example](./table.png) ![Basic empty table 29example](./table-empty.png) 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![Table sort example](./table-sort.png) 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![Table row expand example](./table-expand-row.png) 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![Table search example](./table-search.png) 235 236![Table search active example](./table-search-active.png) 237 238![Table search empty example](./table-search-empty.png) 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![Table row actions example](./table-row-actions.png) 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![Table filter example](./table-filter.png) 400 401![Table filter active example](./table-filter-active.png) 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![Table batch action example](./table-batch-action.png) 492 493![Table batch action active example](./table-batch-action-active.png) 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![Table pagination example](./table-pagination.png) 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```