Interactive demo
<div>
<wui-table head-variant="light"
:items="[
{ age: 40, first_name: 'Dickerson', last_name: 'Macdonald' },
{ age: 21, first_name: 'Larsen', last_name: 'Shaw' },
{ age: 89, first_name: 'Geneva', last_name: 'Wilson' },
{ age: 38, first_name: 'Jami', last_name: 'Carney' }
]"
:fields="[
{
key: 'last_name',
sortable: true
},
{
key: 'first_name',
sortable: true
},
{
key: 'age',
label: 'Person age',
sortable: true,
}
]"
>
</wui-table>
</div>
Anatomy
Key elements

- Table name. Always written in bold.
- Item count overview. Page number | items currently on display | total number of items.
- Pagination. Always in addition to the pagination in the footer. (Default is
light
variant) - Filterbar. (Optional) For more information see filterbar
- Sorting. Sorts the contents of the respective column in ascending () or descending order (). The first click triggers an ascending order for names and numbers (a-z, 1-9) and a descending order for dates (now - 2 days ago).
- TR hover. The hovered row is highlighted in
$color-active-10
- Buttons. The primary buttons appear on hover. The last column of the table is at least as wide as the button. In horizontally scrollable tables, the button can overlap the values of the respective row.
- Links. One column of the table always contains links to jump into the respective object.
- Items per page. The user can choose to see 10, 20 or 50 items within the table. For more information see pagination.
- Subset of all items. In addition to paging, you can also see the currently displayed subset and the total number of items here.
Margins

Widths
An HTML table automatically calculates the best possible column width. The width assigned to each column depends on the length of the content. To have better control over the amount of space a particular content gets, we assign an individual fixed width to each column. This has the further advantage that the content does not “jump” when other items are displayed by filtering, paging, or searching.
When setting the widths, we proceed as follows:
- The TH (
THstyle
) gets one pixel value for thewidth
, themin-width
and themax width
. (e.g.style="width: 120px; min-width: 120px; max-width: 120px;"
) - The most important column (or the column with the most text) gets the
width
of 100% and a min-width with an appropriate pixel value. (e.g.style="width: 100%; min-width: 320px;"
) - Especially for recurring columns (Status, Created etc) we orientate ourselves on already existing tables.
- There are columns for which we know the length (e.g. Created). If you assign a width here, note that it looks good for other languages as well.
The fixed widths only work in combination with the
responsive
prop.
Empty state
If there are no entries, an empty state image is displayed within the table. The ESI in the following example is used when the table does not contain any matching items due to search or filtering. In general, if there are no entries yet we use this image. However, depending on the use case, we also have individual contextual ESIs at this point.
For more information on empty states, see Feedback - every action needs reaction
Note that in this particular case, where there are no entries, the page-size-setter and pagination are disabled.
TH col-1 (Click to sort descending) | TH col-2 | TH col-3 (Click to sort ascending) | ||||
---|---|---|---|---|---|---|
<div class="wui-bs-panel">
<div class="panel-heading">
<div class="wui-inline-list wui-inline-list--md">
<div class="wui-inline-list__item">
<span class="panel-heading__text"><b>Table with empty state image (ESI)</b></span>
</div>
<div class="wui-inline-list__item pull-right">
<ul role="navigation" aria-label="quick table pagination" class="wui-pagination wui-pagination--light">
<li class="wui-pagination__item wui-pagination__item--disabled">
<a aria-label="Previous"><i aria-hidden="true" class="fa fa-chevron-left"></i></a>
</li>
<li class="wui-pagination__item wui-pagination__item--disabled">
<a aria-label="Next"><i aria-hidden="true" class="fa fa-chevron-right"></i></a>
</li>
</ul>
</div>
</div>
</div>
<div class="wui-bs-table--responsive">
<table no-local-filtering="" role="table" aria-busy="false" aria-colcount="7" id="__WVID__26" class="wui-bs-table wui-b-table wui-bs-table--fixed">
<thead role="rowgroup" class="thead-light">
<tr role="row">
<th role="columnheader" scope="col" tabindex="0" aria-colindex="1" aria-sort="">
<div class="position--relative">
<div class="wui-th__label">TH col-1</div>
<span class="sr-only">(Click to sort descending)</span>
</div>
</th>
<th role="columnheader" scope="col" aria-colindex="2">
<div><div class="wui-th__label">TH col-2</div></div>
</th>
<th role="columnheader" scope="col" tabindex="0" aria-colindex="3" aria-sort="none" class="text--center">
<div class="position--relative">
<div class="wui-th__label">TH col-3</div>
<span class="sr-only">(Click to sort ascending)</span>
</div>
</th>
</tr>
</thead>
<tbody role="rowgroup" class="position--relative">
<tr role="row" class="">
<td colspan="7" role="cell">
<div role="alert" aria-live="polite">
<div>
<div title="Your search did not return any hits." class="wui-empty-state">
<h2 class="wui-empty-state__title">Your search did not return any hits.</h2>
<img src="https://cdn.wescale.com/wescale/portal/images/search__empty-state.svg" alt="" class="wui-empty-state__image" />
<div class="wui-empty-state__footer"><a href="#/unit-settings/new" class="wui-btn wui-btn--primary">Primary action (optional)</a></div>
</div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="panel-footer">
<div class="pull-right">
<div class="opacity--6 wui-inline-list wui-inline-list--lg">
<div class="wui-inline-list__item">
<div class="wui-inline-list wui-inline-list--md">
<div class="wui-inline-list__item"><span class="panel-footer__text">Items per page</span></div>
<div class="wui-inline-list__item">
<div class="wui-select__wrapper">
<div class="wui-select wui-select--no-hover wui-select--has-selection wui-select--closed wui-select--disabled wui-select--required">
<div class="wui-select__toggle wui-form-control-group wui-form-control-group--has-icon">
<button tabindex="-1" type="button" title="10" disabled="disabled" class="wui-select__toggle-button wui-form-control"><span class="wui-select__selected-choice">10</span></button>
<a href="javascript:;" tabindex="-1" class="wui-select__caret-icon wui-select__toggle-icon wui-form-control-icon"><i aria-hidden="true" class="fa"></i></a>
</div>
<div class="wui-select__dropdown">
<div style="position: relative; overflow: hidden; width: 100%; height: auto; min-height: 0px; max-height: 175px">
<div style="position: relative; overflow: scroll; margin-right: -17px; margin-bottom: -17px; min-height: 17px; max-height: 192px">
<ul class="wui-select__choices">
<li title="10" class="wui-select__choice wui-select__choice--selected"><span>10</span></li>
<li title="20" class="wui-select__choice"><span>20</span></li>
<li title="50" class="wui-select__choice"><span>50</span></li>
</ul>
</div>
<div><div style="position: relative; display: block; height: 100%; cursor: pointer; border-radius: inherit; background-color: rgba(0, 0, 0, 0.2)"></div></div>
<div style="position: absolute; width: 6px; right: 2px; bottom: 2px; top: 2px; border-radius: 3px">
<div style="position: relative; display: block; width: 100%; cursor: pointer; border-radius: inherit; background-color: rgba(0, 0, 0, 0.2)"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="wui-inline-list__item">
<div class="wui-inline-list wui-inline-list--md">
<div class="wui-inline-list__item"><span class="panel-footer__text">0–0 of 0</span></div>
<div class="wui-inline-list__item">
<ul role="navigation" aria-label="quick table pagination" class="wui-pagination wui-pagination--light">
<li class="wui-pagination__item wui-pagination__item--disabled">
<a aria-label="Previous"><i aria-hidden="true" class="fa fa-chevron-left"></i></a>
</li>
<li class="wui-pagination__item wui-pagination__item--disabled">
<a aria-label="Next"><i aria-hidden="true" class="fa fa-chevron-right"></i></a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
Table cells
Monetary amount
In order to have a better overview of prices, these are wrapped in one line without truncation. In addition, prices are always aligned to the right. To display the currency we use the abbreviation of the international currency code (consisting of 3 letters) and we format according to the CLDR standard.
<div class="border-1 m-auto w-1/2">
<wui-table head-variant="light"
:items="[
{ item: 1, price:'2,652,700.00 EUR' },
{ item: 1, price:'52,650.90 EUR' },
{ item: 1, price:'605,058,184,501,650.00 PLN' }
]"
:fields="[
{
key: 'price',
label: 'Monetary amount',
sortable: true,
tdClass: 'text--right wui-td--content-fit',
thClass: 'text--right'
},
]"
>
</wui-table>
</div>
Labels
Labels help users to quickly grasp the status of a particular item. In tables the labels always come with the inverted
property. Note that labels are truncated after a length of 150px (resp. 120px for smaller screens). When labels are placed one below the other, they all have the same length according to the longest. (see: Chips)
<div class="border-1 m-auto w-1/2">
<wui-table head-variant="light"
:items="[
{ item: 1, label:'highlight' },
{ item: 2, label:'active' }
]"
:fields="[
{
key: 'label',
label: 'Status',
sortable: true,
tdClass: 'wui-td--label'
},
]"
>
<template #cell(label)="data">
<wui-label
variant="active"
inverted
title="Label inverted"
class="wui-td__label">Label inverted</wui-label
>
</template>
</wui-table>
</div>
Custom table layouts
The table can be extended in various ways. Rows and columns can be made sticky, styled separately or grouped, and much more. We can create an entirely new layout that goes beyond the standard display of a table. For example, we used it for Quotation Comparison in our platform. You can find a live-examples in our storybook.

- The panel header should be sticky as it contains essential functions like a search or controls. These actions should always be accessible to the users. For more information on panels, have a look at storybook or style guide.
- Search ( storybook)
- Icon buttons to adjust the settings of the given table ( Icon buttons).
- Filterbar (optional). When used, it is sticky. For more information on our filterbar, have a look at storybook or style guide.
- The table header is sticky and, therefore, never outside the viewport.
- There is a possibility to group several rows within the table body.
- The footer can be sticky to the bottom of the table so that important information is always accessible to user when scrolling inside the table.
- If necessary we can have custom columns.
- Since we expect a lot of data in this table, we use the
virtual-scroller
component as a row wrapper here. This way we can ensure a smooth performance for the user in interactive and large data tables. Also have a look at storybook.
You can find more detailed information about the technical structure and further examples of use in our Storybook page.
WUI decisions 
- Default. Tables are not stripped by default. The hovered row is highlighted in
$color-active-10
- Loading. The table is displayed as a whole as soon as it is completely loaded. When paging through, the items in the table are slightly faded out so that there are no unsightly jumps. We deliberately decided against skeleton loading, as it is technically quite demanding and has relatively little impact for the user.
- Buttons. The primary buttons appear on hover. The last column is always as wide as the button. In horizontally scrollable tables, the button can overlap the values of the respective row.
- Split buttons. When a split button appears on hover, it is always initially collapsed - even if a user has previously expanded it before.
- Sorting. Every table always has a column that is sorted. There is no tri-state for sortings.
- Row height. The height must not exceed 2 lines per table row. You can use
.text-line-clamp-2
to ensure this. - Table Head. Under certain circumstances, there may be icons in the table head.
- Default setup. When you come to a page with an overview-table, the search field in the sticky-header is focused and the table is sorted. Whether it is sorted in ascending or descending order and which column is selected varies from case to case. Often it makes sense to sort according to the "last-updated-column" so that the user always sees the newest item at the top of the table after creating it.
- Table List view. Tables are mainly to be found in the management area. For a certain group of users, such as managers, the tables provide the opportunity to view/edit/create/delete content. However, tables can also occur in the user area. This is particularly the case when a very extensive data set is involved in which certain attributes are to be compared or sorted.
- Responsivness. By setting the prop
responsive
the table becomes horizontally scrollable. Do not use "hidden classes" for columns in this case. When using responsive tables, see also widths.
import {
WuiTable,
WuiTableLite,
WuiTableSimple,
// Helper components
WuiTbody,
WuiThead,
WuiTfoot,
WuiTr,
WuiTd,
WuiTh,
} from "@wui/wui-vue/lib/table";
Description
For displaying tabular data,
<wui-table>
supports pagination, filtering, sorting, custom rendering, various style options, events, and asynchronous data. For simple display of tabular data without all the fancy features, WuiVue provides two lightweight alternative components<wui-table-lite>
and<wui-table-simple>
.
Example: Basic usage
<template>
<div>
<wui-table striped hover :items="items"></wui-table>
</div>
</template>
<script>
export default {
data() {
return {
items: [
{ age: 40, first_name: "Dickerson", last_name: "Macdonald" },
{ age: 21, first_name: "Larsen", last_name: "Shaw" },
{ age: 89, first_name: "Geneva", last_name: "Wilson" },
{ age: 38, first_name: "Jami", last_name: "Carney" },
],
};
},
};
</script>
<!-- wui-table.vue -->
Items (record data)
items
is the table data in array format, where each record (row) data are keyed objects. Example format:
const items = [
{ age: 32, first_name: "Cyndi" },
{ age: 27, first_name: "Havij" },
{ age: 42, first_name: "Robert" },
];
<wui-table>
automatically samples the first row to extract field names (the keys in the record data). Field names are automatically "humanized" by converting kebab-case
, snake_case
, and camelCase
to individual words and capitalizes each word. Example conversions:
first_name
becomesFirst Name
last_name
becomesLast Name
age
becomesAge
YEAR
remainsYEAR
isActive
becomesIs Active
These titles will be displayed in the table header, in the order they appear in the first record of data. See the Fields section below for customizing how field headings appear.
Note: Field order is not guaranteed. Fields will typically appear in the order they were defined in the first row, but this may not always be the case depending on the version of browser in use. See section Fields (column definitions) below to see how to guarantee the order of fields, and to override the headings generated.
Record data may also have additional special reserved name keys for colorizing rows and individual cells (variants), and for triggering additional row detail. The supported optional item record modifier properties (make sure your field keys do not conflict with these names):
Property | Type | Description |
---|---|---|
_cellVariants | Object | WUI contextual state applied to individual cells. Keyed by field (See the Color Variants for supported values). These variants map to classes table-${variant} or bg-${variant} (when the dark prop is set). |
_rowVariant | String | WUI contextual state applied to the entire row (See the Color Variants for supported values). These variants map to classes table-${variant} or bg-${variant} (when the dark prop is set) |
_showDetails | Boolean | Used to trigger the display of the row-details scoped slot. See section Row details support below for additional information |
_isSubRow | Boolean or Object | WUI contextual state applied to the entire row (to mark rows which visually connect to a single previous main row). If the record has its _isSubRow property set to true , the row will be highlighted as a subrow as documented in the style guide. Set the record's _isSubRow property to { isFirst: true } (note the value has to be an object with a sub property isFirst ) to mark the first subrow. |
Example: Using variants for table cells
<template>
<div>
<wui-table hover :items="items"></wui-table>
</div>
</template>
<script>
export default {
data() {
return {
items: [
{ age: 40, first_name: "Dickerson", last_name: "Macdonald" },
{ age: 21, first_name: "Larsen", last_name: "Shaw" },
{
age: 89,
first_name: "Geneva",
last_name: "Wilson",
_rowVariant: "danger",
},
{
age: 40,
first_name: "Thor",
last_name: "MacDonald",
_cellVariants: { age: "info", first_name: "warning" },
},
{ age: 29, first_name: "Dick", last_name: "Dunlap" },
],
};
},
};
</script>
<!-- wui-table-variants.vue -->
items
can also be a reference to a provider function, which returns an Array
of items data. Provider functions can also be asynchronous:
- By returning
null
(orundefined
) and calling a callback, when the data is ready, with the data array as the only argument to the callback, - By returning a
Promise
that resolves to an array.
See the "Using Items Provider functions" section below for more details.
Table item notes and warnings
- Avoid manipulating record data in place, as changes to the underlying items data will cause either the row or entire table to be re-rendered. See Primary Key, below, for ways to minimize Vue's re-rendering of rows.
items
array records should be a simple object and must avoid placing data that may have circular references in the values within a row.<wui-table>
serializes the row data into strings for sorting and filtering, and circular references will cause stack overflows to occur and your app to crash!
Fields (column definitions)
The fields
prop is used to customize the table columns headings, and in which order the columns of data are displayed. The field object keys (i.e. age
or first_name
as shown below) are used to extract the value from each item (record) row, and to provide additional features such as enabling sorting on the column, etc.
Fields can be provided as a simple array or an array of objects. Internally the fields data will be normalized into the array of objects format. Events or slots that include the column field
data will be in the normalized field object format (array of objects for fields
, or an object for an individual field
).
Fields as a simple array
Fields can be a simple array, for defining the order of the columns, and which columns to display:
Example: Using array
fields definition
<template>
<div>
<wui-table striped hover :items="items" :fields="fields"></wui-table>
</div>
</template>
<script>
export default {
data() {
return {
// Note `isActive` is left out and will not appear in the rendered table
fields: ["first_name", "last_name", "age"],
items: [
{
isActive: true,
age: 40,
first_name: "Dickerson",
last_name: "Macdonald",
},
{ isActive: false, age: 21, first_name: "Larsen", last_name: "Shaw" },
{
isActive: false,
age: 89,
first_name: "Geneva",
last_name: "Wilson",
},
{ isActive: true, age: 38, first_name: "Jami", last_name: "Carney" },
],
};
},
};
</script>
<!-- wui-table-fields-array.vue -->
Fields as an array of objects
Fields can be a an array of objects, providing additional control over the fields (such as sorting, formatting, etc.). Only columns (keys) that appear in the fields array will be shown:
Example: Using array of objects fields definition
<template>
<div>
<wui-table striped hover :items="items" :fields="fields"></wui-table>
</div>
</template>
<script>
export default {
data() {
return {
// Note 'isActive' is left out and will not appear in the rendered table
fields: [
{
key: "last_name",
sortable: true,
},
{
key: "first_name",
sortable: false,
},
{
key: "age",
label: "Person age",
sortable: true,
// Variant applies to the whole column, including the header and footer
variant: "danger",
},
],
items: [
{
isActive: true,
age: 40,
first_name: "Dickerson",
last_name: "Macdonald",
},
{ isActive: false, age: 21, first_name: "Larsen", last_name: "Shaw" },
{
isActive: false,
age: 89,
first_name: "Geneva",
last_name: "Wilson",
},
{ isActive: true, age: 38, first_name: "Jami", last_name: "Carney" },
],
};
},
};
</script>
<!-- wui-table-fields-array-of-objects.vue -->
Field definition reference
The following field properties are recognized:
Property | Type | Description |
---|---|---|
key | String | The key for selecting data from the record in the items array. Required when setting the fields via an array of objects. The key is also used for generating the custom data rendering and custom header and footer slot names. |
label | String | Appears in the columns table header (and footer if foot-clone is set). Defaults to the field's key (in humanized format) if not provided. It's possible to use empty labels by assigning an empty string "" but be sure you also set headerTitle to provide non-sighted users a hint about the column contents. |
headerTitle | String | Text to place on the fields header <th> attribute title . Defaults to no title attribute. |
headerAbbr | String | Text to place on the fields header <th> attribute abbr . Set this to the unabbreviated version of the label (or title) if label (or title) is an abbreviation. Defaults to no abbr attribute. |
class | String or Array | Class name (or array of class names) to add to <th> and <td> in the column. |
formatter | String or Function | A formatter callback function or name of a method in your component, can be used instead of (or in conjunction with) scoped field slots. The formatter will be called with the syntax formatter(value, key, item) . Refer to Custom Data Rendering for more details. |
sortable | Boolean | Enable sorting on this column. Refer to the Sorting Section for more details. |
sortKey | String | Set the value of sortBy for the column in the emitted context when no-local-sorting is true . |
sortDirection | String | Set the initial sort direction on this column when it becomes sorted. Refer to the Change initial sort direction Section for more details. |
sortByFormatted | Boolean or Function | Sort the column by the result of the field's formatter callback function when set to true . Default is false . Boolean has no effect if the field does not have a formatter . Optionally accepts a formatter function reference to format the value for sorting purposes only. Refer to the Sorting Section for more details. |
filterByFormatted | Boolean or Function | Filter the column by the result of the field's formatter callback function when set to true . Default is false . Boolean has no effect if the field does not have a formatter . Optionally accepts a formatter function reference to format the value for filtering purposes only. Refer to the Filtering section for more details. |
tdClass | String or Array or Function | Class name (or array of class names) to add to <tbody> data <td> cells in the column. If custom classes per cell are required, a callback function can be specified instead. The function will be called as tdClass(value, key, item) and it must return an Array or String . |
thClass | String or Array | Class name (or array of class names) to add to this field's <thead> /<tfoot> heading <th> cell. |
thStyle | Object | JavaScript object representing CSS styles you would like to apply to the table <thead> /<tfoot> field <th> . |
variant | String | Apply contextual class to all the <th> and <td> in the column - active , success , info , warning , danger . These variants map to classes thead-${variant} (in the header), table-${variant} (in the body), or bg-${variant} (when the prop dark is set). |
tdAttr | Object or Function | JavaScript object representing additional attributes to apply to the <tbody> field <td> cell. If custom attributes per cell are required, a callback function can be specified instead. The function will be called as tdAttr(value, key, item) and it must return an Object . |
thAttr | Object or Function | JavaScript object representing additional attributes to apply to the field's <thead> /<tfoot> heading <th> cell. If the field's isRowHeader is set to true , the attributes will also apply to the <tbody> field <th> cell. If custom attributes per cell are required, a callback function can be specified instead. The function will be called as thAttr(value, key, item, type) and it must return an Object . |
isRowHeader | Boolean | When set to true , the field's item data cell will be rendered with <th> rather than the default of <td> . |
stickyColumn | Boolean | When set to true , and the table in responsive mode or has sticky headers, will cause the column to become fixed to the left when the table's horizontal scrollbar is scrolled. See Sticky columns for more details |
actionButtons | Boolean or Object | If this option is set to true , the column gets a special action button handling on mouseover. Optionally a target attribute can be passed with a HTMLElement reference or Vue $ref which includes horizontal scrolling with overflow-y: auto/scroll (actionButtons: { target: this.$ref.customWrapper } ). See action buttons on mouseover for more details. |
Notes:
- Field properties, if not present, default to
null
(falsey) unless otherwise stated above. class
,thClass
,tdClass
etc. will not work with classes that are defined in scoped CSS, unless you are using VueLoader's Deep selector.- For information on the syntax supported by
thStyle
, see Class and Style Bindings in the Vue.js guide. - Any additional properties added to the field definition objects will be left intact - so you can access them via the named scoped slots for custom data, header, and footer rendering.
For information and usage about scoped slots and formatters, refer to the Custom Data Rendering section below.
Feel free to mix and match simple array and object array together:
const fields = [
{ key: "first_name", label: "First" },
{ key: "last_name", label: "Last" },
"age",
"sex",
];
Primary key
<wui-table>
provides an additional prop primary-key
, which you can use to identify the name of the field key that uniquely identifies the row.
The value specified by the primary column key must be either a string
or number
, and must be unique across all rows in the table.
The primary key column does not need to appear in the displayed fields.
Example: use unique item property as primary-key
<template>
<div>
<wui-table
id="table-transition-example"
:items="items"
:fields="fields"
primary-key="a"
></wui-table>
</div>
</template>
<script>
export default {
data() {
return {
items: [
{ a: 2, b: "Two", c: "Moose" },
{ a: 1, b: "Three", c: "Dog" },
{ a: 3, b: "Four", c: "Cat" },
{ a: 4, b: "One", c: "Mouse" },
],
fields: [
{ key: "a", sortable: true },
{ key: "b", sortable: true },
{ key: "c", sortable: true },
],
};
},
};
</script>
Table row ID generation
When provided, the primary-key
will generate a unique ID for each item row <tr>
element. The ID will be in the format of {table-id}__row_{primary-key-value}
, where {table-id}
is the unique ID of the <wui-table>
and {primary-key-value}
is the value of the item's field value for the field specified by primary-key
.
Table render and transition optimization
The primary-key
is also used by <wui-table>
to help Vue optimize the rendering of table rows. Internally, the value of the field key specified by the primary-key
prop is used as the Vue :key
value for each rendered item row <tr>
element.
If you are seeing rendering issue (i.e. tooltips hiding or unexpected subcomponent re-usage when item data changes or data is sorted/filtered/edited) or table row transitions are not working, setting the primary-key
prop (if you have a unique identifier per row) can alleviate these issues.
Specifying the primary-key
column is handy if you are using 3rd party table transitions or drag and drop plugins, as they rely on having a consistent and unique per row :key
value.
If primary-key
is not provided, <wui-table>
will auto-generate keys based on the displayed row's index number (i.e. position in the displayed table rows). This may cause GUI issues such as sub components/elements that are rendering with previous results (i.e. being re-used by Vue's render patch optimization routines). Specifying a primary-key
column can alleviate this issue (or you can place a unique :key
on your element/components in your custom formatted field slots).
Refer to the Table body transition support section for additional details.
Table style options
Table styling
<wui-table>
provides several props to alter the style of the table:
prop | Type | Description |
---|---|---|
striped | Boolean | Add zebra-striping to the table rows within the <tbody> |
bordered | Boolean | For borders on all sides of the table and cells. |
borderless | Boolean | removes inner borders from table. |
outlined | Boolean | For a thin border on all sides of the table. Has no effect if bordered is set. |
small | Boolean | To make tables more compact by cutting cell padding in half. |
hover | Boolean | To enable a hover highlighting state on table rows within a <tbody> |
dark | Boolean | Invert the colors — with light text on dark backgrounds (equivalent to Bootstrap v4 class .table-dark ) |
fixed | Boolean | Generate a table with equal fixed-width columns (table-layout: fixed; ) |
responsive | Boolean or String | Generate a responsive table to make it scroll horizontally. Set to true for an always responsive table, or set it to one of the breakpoints 'sm' , 'md' , 'lg' , or 'xl' to make the table responsive (horizontally scroll) only on screens smaller than the breakpoint. See Responsive tables below for details. |
sticky-header | Boolean or String | Generates a vertically scrollable table with sticky headers. Set to true to enable sticky headers (default table max-height of 300px ), or set it to a string containing a height (with CSS units) to specify a maximum height other than 300px . See the Sticky header section below for details. |
stacked | Boolean or String | Generate a responsive stacked table. Set to true for an always stacked table, or set it to one of the breakpoints 'sm' , 'md' , 'lg' , or 'xl' to make the table visually stacked only on screens smaller than the breakpoint. See Stacked tables below for details. |
caption-top | Boolean | If the table has a caption, and this prop is set to true , the caption will be visually placed above the table. If false (the default), the caption will be visually placed below the table. |
table-variant | String | Give the table an overall theme color variant. |
head-variant | String | Use 'light' or 'dark' to make table header appear light or dark gray, respectively |
foot-variant | String | Use 'light' or 'dark' to make table footer appear light or dark gray, respectively. If not set, head-variant will be used. Has no effect if foot-clone is not set |
foot-clone | Boolean | Turns on the table footer, and defaults with the same contents a the table header |
no-footer-sorting | Boolean | When foot-clone is true and the table is sortable, disables the sorting icons and click behaviour on the footer heading cells. Refer to the Sorting section below for more details. |
no-border-collapse | Boolean | Disables the default of collapsing of the table borders. Mainly for use with sticky headers and/or sticky columns. Will cause the appearance of double borders in some situations. |
Note: The table style options fixed
, stacked
, caption-top
, no-border-collapse
, sticky headers, sticky columns and the table sorting feature, all require WuiVue's custom CSS.
Example: Basic table styles
<template>
<div>
<wui-form-group
label="Table Options"
label-cols-lg="2"
v-slot="{ ariaDescribedby }"
>
<wui-form-checkbox
v-model="striped"
:aria-describedby="ariaDescribedby"
inline
>Striped</wui-form-checkbox
>
<wui-form-checkbox
v-model="bordered"
:aria-describedby="ariaDescribedby"
inline
>Bordered</wui-form-checkbox
>
<wui-form-checkbox
v-model="borderless"
:aria-describedby="ariaDescribedby"
inline
>Borderless</wui-form-checkbox
>
<wui-form-checkbox
v-model="outlined"
:aria-describedby="ariaDescribedby"
inline
>Outlined</wui-form-checkbox
>
<wui-form-checkbox
v-model="small"
:aria-describedby="ariaDescribedby"
inline
>Small</wui-form-checkbox
>
<wui-form-checkbox
v-model="hover"
:aria-describedby="ariaDescribedby"
inline
>Hover</wui-form-checkbox
>
<wui-form-checkbox
v-model="dark"
:aria-describedby="ariaDescribedby"
inline
>Dark</wui-form-checkbox
>
<wui-form-checkbox
v-model="fixed"
:aria-describedby="ariaDescribedby"
inline
>Fixed</wui-form-checkbox
>
<wui-form-checkbox
v-model="footClone"
:aria-describedby="ariaDescribedby"
inline
>Foot Clone</wui-form-checkbox
>
<wui-form-checkbox
v-model="noCollapse"
:aria-describedby="ariaDescribedby"
inline
>No border collapse</wui-form-checkbox
>
</wui-form-group>
<wui-form-group
label="Head Variant"
label-cols-lg="2"
v-slot="{ ariaDescribedby }"
>
<wui-form-radio-group
v-model="headVariant"
:aria-describedby="ariaDescribedby"
class="m-t-10"
>
<wui-form-radio :value="null" inline>None</wui-form-radio>
<wui-form-radio value="light" inline>Light</wui-form-radio>
<wui-form-radio value="dark" inline>Dark</wui-form-radio>
</wui-form-radio-group>
</wui-form-group>
<wui-form-group
label="Table Variant"
label-for="table-style-variant"
label-cols-lg="2"
>
<wui-form-select
id="table-style-variant"
v-model="tableVariant"
:options="tableVariants"
>
<template #first>
<option value="">-- None --</option>
</template>
</wui-form-select>
</wui-form-group>
<wui-table
:striped="striped"
:bordered="bordered"
:borderless="borderless"
:outlined="outlined"
:small="small"
:hover="hover"
:dark="dark"
:fixed="fixed"
:foot-clone="footClone"
:no-border-collapse="noCollapse"
:items="items"
:fields="fields"
:head-variant="headVariant"
:table-variant="tableVariant"
></wui-table>
</div>
</template>
<script>
export default {
data() {
return {
fields: ["first_name", "last_name", "age"],
items: [
{ age: 40, first_name: "Dickerson", last_name: "Macdonald" },
{ age: 21, first_name: "Larsen", last_name: "Shaw" },
{ age: 89, first_name: "Geneva", last_name: "Wilson" },
],
tableVariants: [
"primary",
"secondary",
"info",
"danger",
"warning",
"success",
"light",
"dark",
],
striped: false,
bordered: false,
borderless: false,
outlined: false,
small: false,
hover: false,
dark: false,
fixed: false,
footClone: false,
headVariant: null,
tableVariant: "",
noCollapse: false,
};
},
};
</script>
<!-- wui-table-bordered.vue -->
Row styling and attributes
You can also style every row using the tbody-tr-class
prop, and optionally supply additional attributes via the tbody-tr-attr
prop:
Property | Type | Description |
---|---|---|
tbody-tr-class | String, Array or Function | Classes to be applied to every row on the table. If a function is given, it will be called as tbodyTrClass( item, type ) and it may return an Array , Object or String . |
tbody-tr-attr | Object or Function | Attributes to be applied to every row on the table. If a function is given, it will be called as tbodyTrAttr( item, type ) and it must return an Object . |
When passing a function reference to tbody-tr-class
or tbody-tr-attr
, the function's arguments will be as follows:
item
- The item record data associated with the row. For rows that are not associated with an item record, this value will benull
orundefined
type
- The type of row being rendered.'row'
for an item row,'row-details'
for an item details row,'row-top'
for the fixed row top slot,'row-bottom'
for the fixed row bottom slot, or'table-busy'
for the table busy slot.
Example: Basic row styles
<template>
<div>
<wui-table
:items="items"
:fields="fields"
:tbody-tr-class="rowClass"
></wui-table>
</div>
</template>
<script>
export default {
data() {
return {
fields: ["first_name", "last_name", "age"],
items: [
{
age: 40,
first_name: "Dickerson",
last_name: "Macdonald",
status: "awesome",
},
{ age: 21, first_name: "Larsen", last_name: "Shaw" },
{ age: 89, first_name: "Geneva", last_name: "Wilson" },
],
};
},
methods: {
rowClass(item, type) {
if (!item || type !== "row") return;
if (item.status === "awesome") return "table-success";
},
},
};
</script>
<!-- wui-table-styled-row.vue -->
Responsive tables
Responsive tables allow tables to be scrolled horizontally with ease. Make any table responsive across all viewports by setting the prop responsive
to true
. Or, pick a maximum breakpoint with which to have a responsive table up to by setting the prop responsive
to one of the breakpoint values: sm
, md
, lg
, or xl
.
Example: Always responsive table
<template>
<div>
<wui-table responsive :items="items"></wui-table>
</div>
</template>
<script>
export default {
data() {
return {
items: [
{
heading1: "table cell",
heading2: "table cell",
heading3: "table cell",
heading4: "table cell",
heading5: "table cell",
heading6: "table cell",
heading7: "table cell",
heading8: "table cell",
heading9: "table cell",
heading10: "table cell",
heading11: "table cell",
heading12: "table cell",
},
{
heading1: "table cell",
heading2: "table cell",
heading3: "table cell",
heading4: "table cell",
heading5: "table cell",
heading6: "table cell",
heading7: "table cell",
heading8: "table cell",
heading9: "table cell",
heading10: "table cell",
heading11: "table cell",
heading12: "table cell",
},
{
heading1: "table cell",
heading2: "table cell",
heading3: "table cell",
heading4: "table cell",
heading5: "table cell",
heading6: "table cell",
heading7: "table cell",
heading8: "table cell",
heading9: "table cell",
heading10: "table cell",
heading11: "table cell",
heading12: "table cell",
},
],
};
},
};
</script>
<!-- wui-table-responsive.vue -->
Responsive table notes:
- Possible vertical clipping/truncation. Responsive tables make use of
overflow-y: hidden
, which clips off any content that goes beyond the bottom or top edges of the table. In particular, this may clip off dropdown menus and other third-party widgets. - Using props
responsive
andfixed
together will not work as expected. Fixed table layout uses the first row (table header in this case) to compute the width required by each column (and the overall table width) to fit within the width of the parent container — without taking cells in the<tbody>
into consideration — resulting in table that may not be responsive. To get around this limitation, you would need to specify widths for the columns (or certain columns) via one of the following methods:- Use
<col>
elements within thetable-colgroup
slot that have widths set (e.g.<col style="width: 20rem">
), or - Wrap header cells in
<div>
elements, via the use of custom header rendering, which have a minimum width set on them, or - Use the
thStyle
property of the field definition object to set a width for the column(s), or - Use custom CSS to define classes to apply to the columns to set widths, via the
thClass
orclass
properties of the field definition object.
- Use
Stacked tables
An alternative to responsive tables, WuiVue includes the stacked table option (using custom SCSS/CSS), which allow tables to be rendered in a visually stacked format. Make any table stacked across all viewports by setting the prop stacked
to true
. Or, alternatively, set a breakpoint at which the table will return to normal table format by setting the prop stacked
to one of the breakpoint values 'sm'
, 'md'
, 'lg'
, or 'xl'
.
Column header labels will be rendered to the left of each field value using a CSS ::before
pseudo element, with a width of 40%.
The stacked
prop takes precedence over the sticky-header
prop and the stickyColumn
field definition property.
Example: Always stacked table
<template>
<div>
<wui-table stacked :items="items"></wui-table>
</div>
</template>
<script>
export default {
data() {
return {
items: [
{ age: 40, first_name: "Dickerson", last_name: "Macdonald" },
{ age: 21, first_name: "Larsen", last_name: "Shaw" },
{ age: 89, first_name: "Geneva", last_name: "Wilson" },
],
};
},
};
</script>
<!-- wui-table-stacked.vue -->
Note: When the table is visually stacked:
- The table header (and table footer) will be hidden.
- Custom rendered header slots will not be shown, rather, the fields'
label
will be used. - The table cannot be sorted by clicking the rendered field labels. You will need to provide an external control to select the field to sort by and the sort direction. See the Sorting section below for sorting control information, as well as the complete example at the bottom of this page for an example of controlling sorting via the use of form controls.
- The slots
top-row
andbottom-row
will be hidden when visually stacked. - The table caption, if provided, will always appear at the top of the table when visually stacked.
- In an always stacked table, the table header and footer, and the fixed top and bottom row slots will not be rendered.
WuiVue's custom CSS is required in order to support stacked tables.
Table caption
Add an optional caption to your table via the prop caption
or the named slot table-caption
(the slot takes precedence over the prop). The default WUI styling places the caption at the bottom of the table:
<template>
<div>
<wui-table :items="items" :fields="fields">
<template #table-caption>This is a table caption.</template>
</wui-table>
</div>
</template>
<script>
export default {
data() {
return {
fields: ["first_name", "last_name", "age"],
items: [
{ age: 40, first_name: "Dickerson", last_name: "Macdonald" },
{ age: 21, first_name: "Larsen", last_name: "Shaw" },
{ age: 89, first_name: "Geneva", last_name: "Wilson" },
],
};
},
};
</script>
<!-- wui-table-caption.vue -->
You can have the caption placed at the top of the table by setting the caption-top
prop to true
:
<template>
<div>
<wui-table :items="items" :fields="fields" caption-top>
<template #table-caption>This is a table caption at the top.</template>
</wui-table>
</div>
</template>
<script>
export default {
data() {
return {
fields: ["first_name", "last_name", "age"],
items: [
{ age: 40, first_name: "Dickerson", last_name: "Macdonald" },
{ age: 21, first_name: "Larsen", last_name: "Shaw" },
{ age: 89, first_name: "Geneva", last_name: "Wilson" },
],
};
},
};
</script>
<!-- wui-table-caption-top.vue -->
You can also use custom CSS to control the caption positioning.
Table colgroup
Use the named slot table-colgroup
to specify <colgroup>
and <col>
elements for optional grouping and styling of table columns. Note the styles available via <col>
elements are limited. Refer to MDN for details and usage of <colgroup>
Slot table-colgroup
can be optionally scoped, receiving an object with the following properties:
Property | Type | Description |
---|---|---|
columns | Number | The number of columns in the rendered table |
fields | Array | Array of field definition objects (normalized to the array of objects format) |
When provided, the content of the table-colgroup
slot will be placed inside of a <colgroup>
element. there is no need to provide your own outer <colgroup>
element. When a series of table columns should be grouped for assistive technology reasons (for conveying logical column associations, use a <col span="#">
element (with #
replaced with the number of grouped columns) to group the series of columns.
Tip: In some situations when trying to set column widths via style
or class
on the <col>
element, you may find that placing the table in fixed
header width (table fixed layout mode) mode, combined with responsive
(horizontal scrolling) mode will help, although you will need to have explicit widths, or minimum widths, via a style or a class for each column's respective <col>
element. For example:
<wui-table fixed responsive :items="items" :fields="fields" ...>
<template #table-colgroup="scope">
<col
v-for="field in scope.fields"
:key="field.key"
:style="{ width: field.key === 'foo' ? '120px' : '180px' }"
/>
</template>
<!-- additional table slots here if needed -->
</wui-table>
Table busy state
<wui-table>
provides a busy
prop that will flag the table as busy, which you can set to true
just before you update your items, and then set it to false
once you have your items. When in the busy state, the table will have the attribute aria-busy="true"
.
During the busy state, the table will be rendered in a "muted" look (opacity: 0.6
), using the following custom CSS:
/* Busy table styling */
table.wui-table[aria-busy="true"] {
opacity: 0.6;
}
You can override this styling using your own CSS.
You may optionally provide a table-busy
slot to show a custom loading message or spinner whenever the table's busy state is true
. The slot will be placed in a <tr>
element with class wui-table-busy-slot
, which has one single <td>
with a colspan
set to the number of fields.
Example of table-busy
slot usage:
<template>
<div>
<wui-button @click="toggleBusy">Toggle Busy State</wui-button>
<wui-table :items="items" :busy="isBusy" class="m-t-15" outlined>
<template #table-busy>
<div class="text-center text-danger m-y-10">
<wui-spinner class="align-middle"></wui-spinner>
<strong>Loading...</strong>
</div>
</template>
</wui-table>
</div>
</template>
<script>
export default {
data() {
return {
isBusy: false,
items: [
{ first_name: "Dickerson", last_name: "MacDonald", age: 40 },
{ first_name: "Larsen", last_name: "Shaw", age: 21 },
{ first_name: "Geneva", last_name: "Wilson", age: 89 },
{ first_name: "Jami", last_name: "Carney", age: 38 },
],
};
},
methods: {
toggleBusy() {
this.isBusy = !this.isBusy;
},
},
};
</script>
<!-- wui-table-busy-slot.vue -->
Also see the Using Items Provider Functions below for additional information on the busy
state.
Notes:
- All click related and hover events, and sort-changed events will not be emitted when the table is in the
busy
state. - Busy styling and slot are not available in the
<wui-table-lite>
component.
Custom data rendering
Custom rendering for each data field in a row is possible using either scoped slots or a formatter callback function, or a combination of both.
Scoped field slots
Scoped field slots give you greater control over how the record data appears. You can use scoped slots to provided custom rendering for a particular field. If you want to add an extra field which does not exist in the records, just add it to the fields
array, and then reference the field(s) in the scoped slot(s). Scoped field slots use the following naming syntax: 'cell(' + field key + ')'
.
You can use the default fall-back scoped slot 'cell()'
to format any cells that do not have an explicit scoped slot provided.
Example: Custom data rendering with scoped slots
<template>
<div>
<wui-table small :fields="fields" :items="items" responsive="sm">
<!-- A virtual column -->
<template #cell(index)="data"> { data.index + 1 } </template>
<!-- A custom formatted column -->
<template #cell(name)="data">
<b class="wui-color--info">{ data.value.last.toUpperCase() }</b>,
<b>{ data.value.first }</b>
</template>
<!-- A virtual composite column -->
<template #cell(nameage)="data">
{ data.item.name.first } is { data.item.age } years old
</template>
<!-- Optional default data cell scoped slot -->
<template #cell()="data">
<i>{ data.value }</i>
</template>
</wui-table>
</div>
</template>
<script>
export default {
data() {
return {
fields: [
// A virtual column that doesn't exist in items
"index",
// A column that needs custom formatting
{ key: "name", label: "Full Name" },
// A regular column
"age",
// A regular column
"sex",
// A virtual column made up from two fields
{ key: "nameage", label: "First name and age" },
],
items: [
{ name: { first: "John", last: "Doe" }, sex: "Male", age: 42 },
{ name: { first: "Jane", last: "Doe" }, sex: "Female", age: 36 },
{ name: { first: "Rubin", last: "Kincade" }, sex: "Male", age: 73 },
{
name: { first: "Shirley", last: "Partridge" },
sex: "Female",
age: 62,
},
],
};
},
};
</script>
<!-- wui-table-data-slots.vue -->
The slot's scope variable (data
in the above sample) will have the following properties:
Property | Type | Description |
---|---|---|
index | Number | The row number (indexed from zero) relative to the displayed rows |
item | Object | The entire raw record data (i.e. items[index] ) for this row (before any formatter is applied) |
value | Any | The value for this key in the record (null or undefined if a virtual column), or the output of the field's formatter function |
unformatted | Any | The raw value for this key in the item record (null or undefined if a virtual column), before being passed to the field's formatter function |
field | Object | The field's normalized field definition object |
detailsShowing | Boolean | Will be true if the row's row-details scoped slot is visible. See section Row details support below for additional information |
toggleDetails | Function | Can be called to toggle the visibility of the rows row-details scoped slot. See section Row details support below for additional information |
rowSelected | Boolean | Will be true if the row has been selected. See section Row select support for additional information |
selectRow | Function | When called, selects the current row. See section Row select support for additional information |
setRowHovered | Function | When called with parameter true , sets the current row in table-hover state (mouseover background color is applied, mouseleave event will not remove this state). When called with false , removes the hover state from current row. See section Action buttons on mouseover for an example |
unselectRow | Function | When called, unselects the current row. See section Row select support for additional information |
Notes:
index
will not always be the actual row's index number, as it is computed after filtering, sorting and pagination have been applied to the original table data. Theindex
value will refer to the displayed row number. This number will align with the indexes from the optionalv-model
bound variable.- When using the new Vue 2.6
v-slot
syntax, note that slot names cannot contain spaces, and when using in-browser DOM templates the slot names will always be lower cased. To get around this, you can pass the slot name using Vue's dynamic slot names
Displaying raw HTML
By default wui-table
escapes HTML tags in items data and results of formatter functions, if you need to display raw HTML code in wui-table
, you should use v-html
directive on an element in a in scoped field slot.
<template>
<div>
<wui-table :items="items">
<template #cell(html)="data">
<span v-html="data.value"></span>
</template>
</wui-table>
</div>
</template>
<script>
export default {
data() {
return {
items: [
{
text: "This is <i>escaped</i> content",
html: 'This is <i>raw <strong>HTML</strong></i> <span style="color:red">content</span>',
},
],
};
},
};
</script>
<!-- wui-table-html-data-slots.vue -->
Warning: Be cautious of using the v-html
method to display user supplied content, as it may make your application vulnerable to XSS attacks, if you do not first sanitize the user supplied string.
Formatter callback
Optionally, you can customize field output by using a formatter callback function. To enable this, the field's formatter
property is used. The value of this property may be String or function reference. In case of a String value, the function must be defined at the parent component's methods. When providing formatter
as a Function
, it must be declared at global scope (window or as global mixin at Vue, or as an anonymous function), unless it has been bound to a this
context.
The callback function accepts three arguments - value
, key
, and item
, and should return the formatted value as a string (HTML strings are not supported)
Example: Custom data rendering with formatter callback function
<template>
<div>
<wui-table :fields="fields" :items="items">
<template #cell(name)="data">
<!-- `data.value` is the value after formatted by the Formatter -->
<a :href="`#${data.value.replace(/[^a-z]+/i,'-').toLowerCase()}`"
>{ data.value }</a
>
</template>
</wui-table>
</div>
</template>
<script>
export default {
data() {
return {
fields: [
{
// A column that needs custom formatting,
// calling formatter 'fullName' in this app
key: "name",
label: "Full Name",
formatter: "fullName",
},
// A regular column
"age",
{
// A regular column with custom formatter
key: "sex",
formatter: (value) => {
return value.charAt(0).toUpperCase();
},
},
{
// A virtual column with custom formatter
key: "birthYear",
label: "Calculated Birth Year",
formatter: (value, key, item) => {
return new Date().getFullYear() - item.age;
},
},
],
items: [
{ name: { first: "John", last: "Doe" }, sex: "Male", age: 42 },
{ name: { first: "Jane", last: "Doe" }, sex: "Female", age: 36 },
{ name: { first: "Rubin", last: "Kincade" }, sex: "male", age: 73 },
{
name: { first: "Shirley", last: "Partridge" },
sex: "female",
age: 62,
},
],
};
},
methods: {
fullName(value) {
return `${value.first} ${value.last}`;
},
},
};
</script>
<!-- wui-table-data-formatter.vue -->
Header and Footer custom rendering via scoped slots
It is also possible to provide custom rendering for the tables thead
and tfoot
elements. Note by default the table footer is not rendered unless foot-clone
is set to true
.
Scoped slots for the header and footer cells uses a special naming convention of 'head(<fieldkey>)'
and 'foot(<fieldkey>)'
respectively. if a 'foot(...)'
slot for a field is not provided, but a 'head(...)'
slot is provided, then the footer will use the 'head(...)'
slot content.
You can use a default fall-back scoped slot 'head()'
or 'foot()'
to format any header or footer cells that do not have an explicit scoped slot provided.
<template>
<div>
<wui-table :fields="fields" :items="items" foot-clone>
<!-- A custom formatted data column cell -->
<template #cell(name)="data">
{ data.value.first } { data.value.last }
</template>
<!-- A custom formatted header cell for field 'name' -->
<template #head(name)="data">
<span class="wui-color--info">{ data.label.toUpperCase() }</span>
</template>
<!-- A custom formatted footer cell for field 'name' -->
<template #foot(name)="data">
<span class="wui-color--danger">{ data.label }</span>
</template>
<!-- Default fall-back custom formatted footer cell -->
<template #foot()="data">
<i>{ data.label }</i>
</template>
</wui-table>
</div>
</template>
<script>
export default {
data() {
return {
fields: [
// A column that needs custom formatting
{ key: "name", label: "Full Name" },
// A regular column
"age",
// A regular column
"sex",
],
items: [
{ name: { first: "John", last: "Doe" }, sex: "Male", age: 42 },
{ name: { first: "Jane", last: "Doe" }, sex: "Female", age: 36 },
{ name: { first: "Rubin", last: "Kincade" }, sex: "Male", age: 73 },
{
name: { first: "Shirley", last: "Partridge" },
sex: "Female",
age: 62,
},
],
};
},
};
</script>
<!-- wui-table-head-foot-slots.vue -->
The slots can be optionally scoped (data
in the above example), and will have the following properties:
Property | Type | Description |
---|---|---|
column | String | The fields's key value |
field | Object | the field's object (from the fields prop) |
label | String | The fields label value (also available as data.field.label ) |
selectAllRows | Method | Select all rows (applicable if the table is in selectable mode |
clearSelected | Method | Unselect all rows (applicable if the table is in selectable mode |
When placing inputs, buttons, selects or links within a head(...)
or foot(...)
slot, note that head-clicked
event will not be emitted when the input, select, textarea is clicked (unless they are disabled). head-clicked
will never be emitted when clicking on links or buttons inside the scoped slots (even when disabled)
Notes:
- When using the new Vue 2.6
v-slot
syntax, note that slot names cannot contain spaces, and when using in-browser DOM templates the slot names will always be lower cased. To get around this, you can pass the slot name using Vue's dynamic slot names
Adding additional rows to the header
If you wish to add additional rows to the header you may do so via the thead-top
slot. This slot is inserted before the header cells row, and is not automatically encapsulated by <tr>..</tr>
tags. It is recommended to use the WuiVue table helper components, rather than native browser table child elements.
<template>
<div>
<wui-table :items="items" :fields="fields" responsive="sm">
<template #thead-top="data">
<wui-tr>
<wui-th colspan="2"><span class="sr-only">Name and ID</span></wui-th>
<wui-th variant="secondary">Type 1</wui-th>
<wui-th variant="primary" colspan="3">Type 2</wui-th>
<wui-th variant="danger">Type 3</wui-th>
</wui-tr>
</template>
</wui-table>
</div>
</template>
<script>
export default {
data() {
return {
items: [
{
name: "Stephen Hawking",
id: 1,
type1: false,
type2a: true,
type2b: false,
type2c: false,
type3: false,
},
{
name: "Johnny Appleseed",
id: 2,
type1: false,
type2a: true,
type2b: true,
type2c: false,
type3: false,
},
{
name: "George Washington",
id: 3,
type1: false,
type2a: false,
type2b: false,
type2c: false,
type3: true,
},
{
name: "Albert Einstein",
id: 4,
type1: true,
type2a: false,
type2b: false,
type2c: true,
type3: false,
},
{
name: "Isaac Newton",
id: 5,
type1: true,
type2a: true,
type2b: false,
type2c: true,
type3: false,
},
],
fields: [
"name",
{ key: "id", label: "ID" },
{ key: "type1", label: "Type 1" },
{ key: "type2a", label: "Type 2A" },
{ key: "type2b", label: "Type 2B" },
{ key: "type2c", label: "Type 2C" },
{ key: "type3", label: "Type 3" },
],
};
},
};
</script>
<!-- wui-table-thead-top-slot.vue -->
Slot thead-top
can be optionally scoped, receiving an object with the following properties:
Property | Type | Description |
---|---|---|
columns | Number | The number of columns in the rendered table |
fields | Array | Array of field definition objects (normalized to the array of objects format) |
selectAllRows | Method | Select all rows (applicable if the table is in selectable mode |
clearSelected | Method | Unselect all rows (applicable if the table is in selectable mode |
Creating a custom footer
If you need greater layout control of the content of the <tfoot>
, you can use the optionally scoped slot custom-foot
to provide your own rows and cells. Use WuiVue's table helper sub-components <wui-tr>
, <wui-th>
, and <wui-td>
to generate your custom footer layout.
Slot custom-foot
can be optionally scoped, receiving an object with the following properties:
Property | Type | Description |
---|---|---|
columns | Number | The number of columns in the rendered table |
fields | Array | Array of field definition objects (normalized to the array of objects format) |
items | Array | Array of the currently displayed items records - after filtering, sorting and pagination |
Notes:
- The
custom-foot
slot will not be rendered if thefoot-clone
prop has been set. head-clicked
events are not be emitted when clicking oncustom-foot
cells.- Sorting and sorting icons are not available for cells in the
custom-foot
slot. - The custom footer will not be shown when the table is in visually stacked mode.
Custom empty and emptyfiltered rendering via slots
Aside from using empty-text
, empty-filtered-text
, empty-html
, and empty-filtered-html
, it is also possible to provide custom rendering for tables that have no data to display using named slots.
In order for these slots to be shown, the show-empty
attribute must be set and items
must be either falsy or an array of length 0.
<div>
<wui-table :fields="fields" :items="items" show-empty>
<template #empty="scope">
<h4>{ scope.emptyText }</h4>
</template>
<template #emptyfiltered="scope">
<h4>{ scope.emptyFilteredText }</h4>
</template>
</wui-table>
</div>
The slot can optionally be scoped. The slot's scope (scope
in the above example) will have the following properties:
Property | Type | Description |
---|---|---|
emptyHtml | String | The empty-html prop |
emptyText | String | The empty-text prop |
emptyFilteredHtml | String | The empty-filtered-html prop |
emptyFilteredText | String | The empty-filtered-text prop |
fields | Array | The fields prop |
items | Array | The items prop. Exposed here to check null vs [] |
Advanced features
Sticky headers
Use the sticky-header
prop to enable a vertically scrolling table with headers that remain fixed (sticky) as the table body scrolls. Setting the prop to true
(or no explicit value) will generate a table that has a maximum height of 300px
. To specify a maximum height other than 300px
, set the sticky-header
prop to a valid CSS height (including units), i.e. sticky-header="200px"
. Tables with sticky-header
enabled will also automatically become always responsive horizontally, regardless of the responsive
prop setting, if the table is wider than the available horizontal space.
<template>
<div>
<wui-table sticky-header :items="items" head-variant="light"></wui-table>
</div>
</template>
<script>
export default {
data() {
return {
items: [
{
heading1: "table cell",
heading2: "table cell",
heading3: "table cell",
},
{
heading1: "table cell",
heading2: "table cell",
heading3: "table cell",
},
{
heading1: "table cell",
heading2: "table cell",
heading3: "table cell",
},
{
heading1: "table cell",
heading2: "table cell",
heading3: "table cell",
},
{
heading1: "table cell",
heading2: "table cell",
heading3: "table cell",
},
{
heading1: "table cell",
heading2: "table cell",
heading3: "table cell",
},
{
heading1: "table cell",
heading2: "table cell",
heading3: "table cell",
},
{
heading1: "table cell",
heading2: "table cell",
heading3: "table cell",
},
{
heading1: "table cell",
heading2: "table cell",
heading3: "table cell",
},
{
heading1: "table cell",
heading2: "table cell",
heading3: "table cell",
},
{
heading1: "table cell",
heading2: "table cell",
heading3: "table cell",
},
{
heading1: "table cell",
heading2: "table cell",
heading3: "table cell",
},
],
};
},
};
</script>
<!-- wui-table-sticky-header.vue -->
Sticky header notes:
- The
sticky-header
prop has no effect if the table has thestacked
prop set. - Sticky header tables are wrapped inside a vertically scrollable
<div>
with a maximum height set. - WuiVue's custom CSS is required in order to support
sticky-header
. - WUI uses the CSS style
border-collapse: collapsed
on table elements. This prevents the borders on the sticky header from "sticking" to the header, and hence the borders will scroll when the body scrolls. To get around this issue, set the propno-border-collapse
on the table (note that this may cause double width borders when using features such asbordered
, etc.). - The sticky header feature uses CSS style
position: sticky
to position the headings. Internet Explorer does not supportposition: sticky
, hence for IE 11 the table headings will scroll with the table body.
Sticky columns
Columns can be made sticky, where they stick to the left of the table when the table has a horizontal scrollbar. To make a column a sticky column, set the stickyColumn
prop in the field's header definition. Sticky columns will only work when the table has either the sticky-header
prop set and/or the responsive
prop is set.
To have multiple sticky columns we can set any number of cells as sticky columns in each row, and use CSS position left to offset from previous cell. With that setup visually we have layout where any number of columns are fixed to left side when scrolling horizontally.
Example: Sticky columns and headers
<template>
<div>
<div class="m-b-10">
<wui-form-checkbox v-model="stickyHeader" inline
>Sticky header</wui-form-checkbox
>
<wui-form-checkbox v-model="noCollapse" inline
>No border collapse</wui-form-checkbox
>
</div>
<wui-table
:sticky-header="stickyHeader"
:no-border-collapse="noCollapse"
responsive
:items="items"
:fields="fields"
>
<!-- We are using utility class `text-nowrap` to help illustrate horizontal scrolling -->
<template #head(id)="scope">
<div class="text-nowrap">Row ID</div>
</template>
<template #head()="scope">
<div class="text-nowrap">Heading { scope.label }</div>
</template>
</wui-table>
</div>
</template>
<script>
export default {
data() {
return {
stickyHeader: true,
noCollapse: false,
fields: [
{
key: "id",
stickyColumn: true,
isRowHeader: true,
variant: "primary",
},
"a",
"b",
{ key: "c", stickyColumn: true, variant: "info" },
"d",
"e",
"f",
"g",
"h",
"i",
"j",
"k",
"l",
],
items: [
{
id: 1,
a: 0,
b: 1,
c: 2,
d: 3,
e: 4,
f: 5,
g: 6,
h: 7,
i: 8,
j: 9,
k: 10,
l: 11,
},
{
id: 2,
a: 0,
b: 1,
c: 2,
d: 3,
e: 4,
f: 5,
g: 6,
h: 7,
i: 8,
j: 9,
k: 10,
l: 11,
},
{
id: 3,
a: 0,
b: 1,
c: 2,
d: 3,
e: 4,
f: 5,
g: 6,
h: 7,
i: 8,
j: 9,
k: 10,
l: 11,
},
{
id: 4,
a: 0,
b: 1,
c: 2,
d: 3,
e: 4,
f: 5,
g: 6,
h: 7,
i: 8,
j: 9,
k: 10,
l: 11,
},
{
id: 5,
a: 0,
b: 1,
c: 2,
d: 3,
e: 4,
f: 5,
g: 6,
h: 7,
i: 8,
j: 9,
k: 10,
l: 11,
},
{
id: 6,
a: 0,
b: 1,
c: 2,
d: 3,
e: 4,
f: 5,
g: 6,
h: 7,
i: 8,
j: 9,
k: 10,
l: 11,
},
{
id: 7,
a: 0,
b: 1,
c: 2,
d: 3,
e: 4,
f: 5,
g: 6,
h: 7,
i: 8,
j: 9,
k: 10,
l: 11,
},
{
id: 8,
a: 0,
b: 1,
c: 2,
d: 3,
e: 4,
f: 5,
g: 6,
h: 7,
i: 8,
j: 9,
k: 10,
l: 11,
},
{
id: 9,
a: 0,
b: 1,
c: 2,
d: 3,
e: 4,
f: 5,
g: 6,
h: 7,
i: 8,
j: 9,
k: 10,
l: 11,
},
{
id: 10,
a: 0,
b: 1,
c: 2,
d: 3,
e: 4,
f: 5,
g: 6,
h: 7,
i: 8,
j: 9,
k: 10,
l: 11,
},
],
};
},
};
</script>
<!-- table-sticky-columns.vue -->
Sticky column notes:
- Sticky columns has no effect if the table has the
stacked
prop set. - Sticky columns tables require either the
sticky-header
and/orresponsive
modes, and are wrapped inside a horizontally scrollable<div>
. - When you have multiple columns that are set as
stickyColumn
, the columns will stack over each other visually, and the left-most sticky columns may "peek" out from under the next sticky column. To get around this behaviour, make sure your latter sticky columns are the same width or wider than previous sticky columns. - WUI uses the CSS style
border-collapse: collapsed
on table elements. This prevents any borders on the sticky columns from "sticking" to the column, and hence those borders will scroll when the body scrolls. To get around this issue, set the propno-border-collapse
on the table (note that this may cause double width borders when using features such asbordered
, etc.). - WuiVue's custom CSS is required in order to support sticky columns.
- The sticky column feature uses CSS style
position: sticky
to position the column cells. Internet Explorer does not supportposition: sticky
, hence for IE 11 the sticky column will scroll with the table body.
Infinite scrolling
Use the infinite
prop to enable infinite scrolling of a table. Tables with infinite
enabled also automatically have a sticky-header. Infinite scrolling requires that you pass an items provider function to the prop items
. The context with which the provider function is called also contains the property ctx.scrollBottomTriggerCounter
which contains the number of already triggered scroll-bottom
events. You can use this number to calculate the offset and limit on your API call.
<template>
<div>
<wui-table infinite ...></wui-table>
</div>
</template>
Infinite scrolling notes:
- Infinite scroll tables are wrapped inside a vertically scrollable
<div>
with a maximum height set. - The
currentPage
prop has no effect, usectx.scrollBottomTriggerCounter
of the provider function context to get the number of triggered reloads (scroll-bottom
events) - You have to use an items provider function
Performance enhancement
For large data sets where we have multiple rows (500+) we may experience performance issues. Performance issues can be more noticeable if our table contains interactive component that can cause table rerendering. To improve performance in those cases we can use component wui-virtual-scroller
documentation. More advanced example is available on Storybook for "Quotation comparison" table, and also under documentation for <wui-table-simple>
.
Row details support
If you would optionally like to display additional record information (such as columns not specified in the fields definition array), you can use the scoped slot row-details
, in combination with the special item record Boolean property _showDetails
.
If the record has its _showDetails
property set to true
, and a row-details
scoped slot exists, a new row will be shown just below the item, with the rendered contents of the row-details
scoped slot.
In the scoped field slot, you can toggle the visibility of the row's row-details
scoped slot by calling the toggleDetails
function passed to the field's scoped slot variable. You can use the scoped fields slot variable detailsShowing
to determine the visibility of the row-details
slot.
Note: If manipulating the _showDetails
property directly on the item data (i.e. not via the toggleDetails
function reference), the _showDetails
properly must exist in the items data for proper reactive detection of changes to its value. Read more about Vue's reactivity limitations.
Available row-details
scoped variable properties:
Property | Type | Description |
---|---|---|
item | Object | The entire row record data object |
index | Number | The current visible row number |
fields | Array | The normalized fields definition array (in the array of objects format) |
toggleDetails | Function | Function to toggle visibility of the row's details slot |
rowSelected | Boolean | Will be true if the row has been selected. See section Row select support for additional information |
selectRow | Function | When called, selects the current row. See section Row select support for additional information |
unselectRow | Function | When called, unselects the current row. See section Row select support for additional information |
Note: the row select related scope properties are only available in <wui-table>
.
In the following example, we show two methods of toggling the visibility of the details: one via a button, and one via a checkbox. We also have the third row details defaulting to have details initially showing.
<template>
<div>
<wui-table :items="items" :fields="fields" striped responsive="sm">
<template #cell(show_details)="row">
<wui-button size="sm" @click="row.toggleDetails" class="m-r-10">
{ row.detailsShowing ? 'Hide' : 'Show'} Details
</wui-button>
<!-- As `row.showDetails` is one-way, we call the toggleDetails function on @change -->
<wui-form-checkbox
v-model="row.detailsShowing"
@change="row.toggleDetails"
>
Details via check
</wui-form-checkbox>
</template>
<template #row-details="row">
<wui-panel>
<wui-row class="m-b-10">
<wui-col sm="3" class="text-sm-right"><b>Age:</b></wui-col>
<wui-col>{ row.item.age }</wui-col>
</wui-row>
<wui-row class="m-b-10">
<wui-col sm="3" class="text-sm-right"><b>Is Active:</b></wui-col>
<wui-col>{ row.item.isActive }</wui-col>
</wui-row>
<wui-button size="sm" @click="row.toggleDetails"
>Hide Details</wui-button
>
</wui-panel>
</template>
</wui-table>
</div>
</template>
<script>
export default {
data() {
return {
fields: ["first_name", "last_name", "show_details"],
items: [
{
isActive: true,
age: 40,
first_name: "Dickerson",
last_name: "Macdonald",
},
{ isActive: false, age: 21, first_name: "Larsen", last_name: "Shaw" },
{
isActive: false,
age: 89,
first_name: "Geneva",
last_name: "Wilson",
_showDetails: true,
},
{ isActive: true, age: 38, first_name: "Jami", last_name: "Carney" },
],
};
},
};
</script>
<!-- wui-table-details.vue -->
Row select support
You can make rows selectable, by using the <wui-table>
prop selectable
.
Users can easily change the selecting mode by setting the select-mode
prop.
'multi'
: Each click will select/deselect the row (default mode)'single'
: Only a single row can be selected at one time'range'
: Any row clicked is selected, any other deselected. Shift + click selects a range of rows, and Ctrl (or Cmd) + click will toggle the selected row.
When a table is selectable
and the user clicks on a row, <wui-table>
will emit the row-selected
event, passing a single argument which is the complete list of selected items. Treat this argument as read-only.
Rows can also be programmatically selected and unselected via the following exposed methods on the <wui-table>
instance (i.e. via a reference to the table instance via this.$refs
):
Method | Description |
---|---|
selectRow(index) | Selects a row with the given index number. |
unselectRow(index) | Unselects a row with the given index number. |
selectAllRows() | Selects all rows in the table, except in single mode in which case only the first row is selected. |
clearSelected() | Unselects all rows. |
isRowSelected(index) | Returns true if the row with the given index is selected, otherwise it returns false . |
Programmatic row selection notes:
index
is the zero-based index of the table's visible rows, after filtering, sorting, and pagination have been applied.- In
single
mode,selectRow(index)
will unselect any previous selected row. - Attempting to
selectRow(index)
orunselectRow(index)
on a non-existent row will be ignored. - The table must be
selectable
for any of these methods to have effect. - You can disable selection of rows via click events by setting the
no-select-on-click
prop. Rows will then only be selectable programmatically.
Row select notes:
- Sorting, filtering, or paginating the table will clear the active selection. The
row-selected
event will be emitted with an empty array ([]
) if needed. - When the table is in
selectable
mode, all data item<tr>
elements will be in the document tab sequence (tabindex="0"
) for accessibility reasons, and will have the attributearia-selected
set to either'true'
or'false'
depending on the selected state of the row. - When a table is
selectable
, the table will have the attributearia-multiselect
set to either'false'
forsingle
mode, and'true'
for eithermulti
orrange
modes.
When a <wui-table>
is selectable, it will have class wui-table-selectable
and one of the following three classes (depending on which mode is in use) on the <table>
element:
wui-table-select-single
wui-table-select-multi
wui-table-select-range
When at least one row is selected, the class wui-table-selecting
will be active on the <table>
element. Rows that are selected rows will have a class of wui-table-row-selected
applied to the <tr>
element.
Use the prop selected-variant
to apply a WUI theme color to the selected row(s). Note, due to the order that the table variants are defined in WUI's CSS, any row-variant might take precedence over the selected-variant
. You can set selected-variant
to an empty string if you will be using other means to convey that a row is selected (such as a scoped field slot in the below example).
The selected-variant
can be any of the standard (or custom) WUI base color variants, or the special table active
variant (the default) which takes precedence over any specific row or cell variants.
For accessibility reasons (specifically for color blind users, or users with color contrast issues), it is highly recommended to always provide some other visual means of conveying that a row is selected, such as a virtual column as shown in the example below.
<template>
<div>
<wui-form-group
label="Selection mode:"
label-for="table-select-mode-select"
label-cols-md="4"
>
<wui-form-select
id="table-select-mode-select"
v-model="selectMode"
:options="modes"
class="m-b-10"
></wui-form-select>
</wui-form-group>
<wui-table
:items="items"
:fields="fields"
:select-mode="selectMode"
responsive="sm"
ref="selectableTable"
selectable
@row-selected="onRowSelected"
>
<!-- Example scoped slot for select state illustrative purposes -->
<template #cell(selected)="{ rowSelected }">
<template v-if="rowSelected">
<span aria-hidden="true">✓</span>
<span class="sr-only">Selected</span>
</template>
<template v-else>
<span aria-hidden="true"> </span>
<span class="sr-only">Not selected</span>
</template>
</template>
</wui-table>
<p>
<wui-button size="sm" @click="selectAllRows">Select all</wui-button>
<wui-button size="sm" @click="clearSelected">Clear selected</wui-button>
<wui-button size="sm" @click="selectThirdRow">Select 3rd row</wui-button>
<wui-button size="sm" @click="unselectThirdRow"
>Unselect 3rd row</wui-button
>
</p>
<p>
Selected Rows:<br />
{ selected }
</p>
</div>
</template>
<script>
export default {
data() {
return {
modes: ["multi", "single", "range"],
fields: ["selected", "isActive", "age", "first_name", "last_name"],
items: [
{
isActive: true,
age: 40,
first_name: "Dickerson",
last_name: "Macdonald",
},
{ isActive: false, age: 21, first_name: "Larsen", last_name: "Shaw" },
{
isActive: false,
age: 89,
first_name: "Geneva",
last_name: "Wilson",
},
{ isActive: true, age: 38, first_name: "Jami", last_name: "Carney" },
],
selectMode: "multi",
selected: [],
};
},
methods: {
onRowSelected(items) {
this.selected = items;
},
selectAllRows() {
this.$refs.selectableTable.selectAllRows();
},
clearSelected() {
this.$refs.selectableTable.clearSelected();
},
selectThirdRow() {
// Rows are indexed from 0, so the third row is index 2
this.$refs.selectableTable.selectRow(2);
},
unselectThirdRow() {
// Rows are indexed from 0, so the third row is index 2
this.$refs.selectableTable.unselectRow(2);
},
},
};
</script>
<!-- wui-table-selectable.vue -->
Action buttons on mouseover
A column with action buttons or action dropdowns like "Show details", "Edit", "Copy" etc. can also be displayed only on mouseover over the table row. To mark a column with action buttons, set the actionButtons
prop in the field's header definition.
Example: Action buttons on mouseover
<template>
<div>
<wui-table responsive hover :items="items" :fields="fields">
<!-- We are using utility class `text--no-wrap` to help illustrate horizontal scrolling -->
<template #head()="scope">
<div class="text--no-wrap">Heading { scope.label }</div>
</template>
<!-- Use the scoped prop function `setRowHovered` to toggle the columns hover state when split button dropdowns opens and closes -->
<template #cell(actions)="{ setRowHovered }">
<div class="display--inline">
<div
class="display--flex align-items-center flex-fixed h-full p-l-20"
>
<wui-button variant="active" class="m-r-20">Action 1</wui-button>
<wui-button variant="active" class="m-r-20">Action 2</wui-button>
<wui-split-button
variant="active"
alignment="right"
@open="setRowHovered(true)"
@close="setRowHovered(false)"
>
<button>Option 1</button>
<wui-button variant="transparent-bg" block class="text--left"
>Option 2</wui-button
>
<wui-button variant="transparent-bg" block class="text--left"
>Option 3</wui-button
>
<wui-button variant="transparent-bg" block class="text--left"
>Option 4</wui-button
>
</wui-split-button>
</div>
</div>
</template>
</wui-table>
</div>
</template>
<script>
export default {
data() {
return {
fields: [
"id",
"a",
"b",
"c",
"d",
"e",
"f",
"g",
"h",
"i",
"j",
"k",
"l",
{ key: "actions", actionButtons: true },
],
items: [
{
id: 1,
a: 0,
b: 1,
c: 2,
d: 3,
e: 4,
f: 5,
g: 6,
h: 7,
i: 8,
j: 9,
k: 10,
l: 11,
},
{
id: 2,
a: 0,
b: 1,
c: 2,
d: 3,
e: 4,
f: 5,
g: 6,
h: 7,
i: 8,
j: 9,
k: 10,
l: 11,
},
{
id: 3,
a: 0,
b: 1,
c: 2,
d: 3,
e: 4,
f: 5,
g: 6,
h: 7,
i: 8,
j: 9,
k: 10,
l: 11,
},
{
id: 4,
a: 0,
b: 1,
c: 2,
d: 3,
e: 4,
f: 5,
g: 6,
h: 7,
i: 8,
j: 9,
k: 10,
l: 11,
},
{
id: 5,
a: 0,
b: 1,
c: 2,
d: 3,
e: 4,
f: 5,
g: 6,
h: 7,
i: 8,
j: 9,
k: 10,
l: 11,
},
{
id: 6,
a: 0,
b: 1,
c: 2,
d: 3,
e: 4,
f: 5,
g: 6,
h: 7,
i: 8,
j: 9,
k: 10,
l: 11,
},
{
id: 7,
a: 0,
b: 1,
c: 2,
d: 3,
e: 4,
f: 5,
g: 6,
h: 7,
i: 8,
j: 9,
k: 10,
l: 11,
},
{
id: 8,
a: 0,
b: 1,
c: 2,
d: 3,
e: 4,
f: 5,
g: 6,
h: 7,
i: 8,
j: 9,
k: 10,
l: 11,
},
{
id: 9,
a: 0,
b: 1,
c: 2,
d: 3,
e: 4,
f: 5,
g: 6,
h: 7,
i: 8,
j: 9,
k: 10,
l: 11,
},
{
id: 10,
a: 0,
b: 1,
c: 2,
d: 3,
e: 4,
f: 5,
g: 6,
h: 7,
i: 8,
j: 9,
k: 10,
l: 11,
},
],
};
},
};
</script>
<!-- table-action-buttons.vue -->
Custom table wrapper for horizontal scrolling
If you have created a custom HTML wrapper for the table with horizontal scrolling (overflow-y: auto
), you must pass this wrapper as a target reference to the actionButtons
prop. The table needs this wrapper to calculate the correct left
position of the floating action buttons.
If no target wrapper is specified (e.g. just actionButtons: true
), then the outermost HTML container rendered by the table component is used. In most cases this should be sufficient.
Example: Action buttons with custom table wrapper
<template>
<div id="my-custom-table-wrapper" ref="wrapper">
<wui-table
v-if="isInitialized"
responsive
hover
:items="items"
:fields="fields"
>
<!-- We are using utility class `text--no-wrap` to help illustrate horizontal scrolling -->
<template #head()="scope">
<div class="text--no-wrap">Heading { scope.label }</div>
</template>
<!-- Use the scoped prop function `setRowHovered` to toggle the columns hover state when split button dropdowns opens and closes -->
<template #cell(actions)="{ setRowHovered }">
<div class="display--inline">
<div
class="display--flex align-items-center flex-fixed h-full p-l-20"
>
<wui-button variant="active" class="m-r-20">Action 1</wui-button>
<wui-button variant="active" class="m-r-20">Action 2</wui-button>
<wui-split-button
variant="active"
alignment="right"
@open="setRowHovered(true)"
@close="setRowHovered(false)"
>
<button>Option 1</button>
<wui-button variant="transparent-bg" block class="text--left"
>Option 2</wui-button
>
<wui-button variant="transparent-bg" block class="text--left"
>Option 3</wui-button
>
<wui-button variant="transparent-bg" block class="text--left"
>Option 4</wui-button
>
</wui-split-button>
</div>
</div>
</template>
</wui-table>
</div>
</template>
<script>
export default {
data() {
return {
isInitialized: false,
items: [
{
a: 0,
b: 1,
c: 2,
},
{
a: 0,
b: 1,
c: 2,
},
{
a: 0,
b: 1,
c: 2,
},
],
};
},
computed: {
fields() {
return [
"a",
"b",
"c",
{ key: "actions", actionButtons: { target: this.$ref.wrapper } },
];
},
},
mounted() {
// We do this to make sure this.$refs.wrapper is available
this.isInitialized = true;
},
};
</script>
<!-- table-action-buttons-custom-wrapper.vue -->
Action button dropdown menus
If you use a dropdown menu inside the action buttons table cell, you must use the setRowHovered
scoped prop function to toggle the hover state of the table row. This prevents the dropdown menu from accidentally disappearing when a table row mouseleave event is triggered (see example above).
Action buttons notes:
- Since the table cell with the action buttons is positioned absolutely and thus influences the layout of the table cell content, it is necessary to align the buttons accordingly, e.g. with Flexbox utils (see code example above).
stickyColumn
is ignored ifactionButtons
prop is setactionButtons
works only for table cells in table bodytbody
, but not for table cells in table headerthead
or table footertfoot
.- Avoid giving the table row a custom background color (e.g. via the
variant
ortbodyTrClass
prop), because the action buttons table cell has a fixed background color (mouseover color). This could lead to an unwanted "double color effect".
Table body transition support
Vue transitions and animations are optionally supported on the <tbody>
element via the use of Vue's <transition-group>
component internally. Three props are available for transitions support (all three default to undefined):
Prop | Type | Description |
---|---|---|
tbody-transition-props | Object | Object of transition-group properties |
tbody-transition-handlers | Object | Object of transition-group event handlers |
primary-key | String | String specifying the field to use as a unique row key (required) |
To enable transitions you need to specify tbody-transition-props
and/or tbody-transition-handlers
, and must specify which field key to use as a unique key via the primary-key
prop. Your data must have a column (specified by setting the primary-key
prop to the name of the field) that has a unique value per row in order for transitions to work properly. The primary-key
field's value can either be a unique string or number. The field specified does not need to appear in the rendered table output, but it must exist in each row of your items data.
You must also provide CSS to handle your transitions (if using CSS transitions) in your project.
For more information of Vue's list rendering transitions, see the Vue JS official docs.
In the example below, we have used the following custom CSS:
table#table-transition-example .flip-list-move {
transition: transform 1s;
}
<template>
<div>
<wui-table
id="table-transition-example"
:items="items"
:fields="fields"
striped
small
primary-key="a"
:tbody-transition-props="transProps"
></wui-table>
</div>
</template>
<script>
export default {
data() {
return {
transProps: {
// Transition name
name: "flip-list",
},
items: [
{ a: 2, b: "Two", c: "Moose" },
{ a: 1, b: "Three", c: "Dog" },
{ a: 3, b: "Four", c: "Cat" },
{ a: 4, b: "One", c: "Mouse" },
],
fields: [
{ key: "a", sortable: true },
{ key: "b", sortable: true },
{ key: "c", sortable: true },
],
};
},
};
</script>
<!-- wui-table-transitions.vue -->
v-model
binding
If you bind a variable to the v-model
prop, the contents of this variable will be the currently displayed item records (zero based index, up to page-size
- 1). This variable (the value
prop) should usually be treated as readonly.
The records within the v-model
are a filtered/paginated shallow copy of items
, and hence any changes to a record's properties in the v-model
will be reflected in the original items
array (except when items
is set to a provider function). Deleting a record from the v-model
array will not remove the record from the original items array nor will it remove it from the displayed rows.
Note: Do not bind any value directly to the value
prop. Use the v-model
binding.
Sorting
As mentioned in the Fields section above, you can make columns sortable in <wui-table>
. Clicking on a sortable column header will sort the column in ascending direction (smallest first), while clicking on it again will switch the direction of sorting to descending (largest first). Clicking on a non-sortable column will clear the sorting (the prop no-sort-reset
can be used to disable this feature).
You can control which column is pre-sorted and the order of sorting (ascending or descending). To pre-specify the column to be sorted, set the sort-by
prop to the field's key. Set the sort direction by setting sort-desc
to either true
(for descending) or false
(for ascending, the default).
- Ascending: Items are sorted lowest to highest (i.e.
A
toZ
) and will be displayed with the lowest value in the first row with progressively higher values in the following rows. - Descending: Items are sorted highest to lowest (i.e.
Z
toA
) and will be displayed with the highest value in the first row with progressively lower values in the following rows.
The props sort-by
and sort-desc
can be turned into two-way (syncable) props by adding the .sync
modifier. Your bound variables will then be updated accordingly based on the current sort criteria. See the Vue docs for details on the .sync
prop modifier.
Setting sort-by
to a column that is not defined in the fields as sortable
will result in the table not being sorted.
When the prop foot-clone
is set, the footer headings will also allow sorting by clicking, even if you have custom formatted footer field headers. To disable the sort icons and sorting via heading clicks in the footer, set the no-footer-sorting
prop to true.
<template>
<div>
<wui-table
:items="items"
:fields="fields"
:sort-by.sync="sortBy"
:sort-desc.sync="sortDesc"
responsive="sm"
></wui-table>
<div>
Sorting By: <b>{ sortBy }</b>, Sort Direction:
<b>{ sortDesc ? 'Descending' : 'Ascending' }</b>
</div>
</div>
</template>
<script>
export default {
data() {
return {
sortBy: "age",
sortDesc: false,
fields: [
{ key: "last_name", sortable: true },
{ key: "first_name", sortable: true },
{ key: "age", sortable: true },
{ key: "isActive", sortable: false },
],
items: [
{
isActive: true,
age: 40,
first_name: "Dickerson",
last_name: "Macdonald",
},
{ isActive: false, age: 21, first_name: "Larsen", last_name: "Shaw" },
{
isActive: false,
age: 89,
first_name: "Geneva",
last_name: "Wilson",
},
{ isActive: true, age: 38, first_name: "Jami", last_name: "Carney" },
],
};
},
};
</script>
<!-- wui-table-sorting.vue -->
Sort icon alignment
By default the sorting icons appear right aligned in the header cell. You can change the icons to be left aligned by setting the prop sort-icon-left
on <wui-table>
.
<template>
<div>
<wui-table
:items="items"
:fields="fields"
:sort-by.sync="sortBy"
:sort-desc.sync="sortDesc"
sort-icon-left
responsive="sm"
></wui-table>
<div>
Sorting By: <b>{ sortBy }</b>, Sort Direction:
<b>{ sortDesc ? 'Descending' : 'Ascending' }</b>
</div>
</div>
</template>
<script>
export default {
data() {
return {
sortBy: "age",
sortDesc: false,
fields: [
{ key: "last_name", sortable: true },
{ key: "first_name", sortable: true },
{ key: "age", sortable: true },
{ key: "isActive", sortable: false },
],
items: [
{
isActive: true,
age: 40,
first_name: "Dickerson",
last_name: "Macdonald",
},
{ isActive: false, age: 21, first_name: "Larsen", last_name: "Shaw" },
{
isActive: false,
age: 89,
first_name: "Geneva",
last_name: "Wilson",
},
{ isActive: true, age: 38, first_name: "Jami", last_name: "Carney" },
],
};
},
};
</script>
<!-- wui-table-sorting-left.vue -->
Customizing the sort icons
The sorting icons are generated via the use of font-awesome icons via CSS. The icons can be altered by updating WuiVue's SASS/SCSS variables and recompiling the SCSS source code. Refer to the theming section for details on customizing Bootstrap and WuiVue's generated CSS.
Sort-compare routine
The internal built-in default sort-compare
function sorts the specified field key
based on the data in the underlying record object (or by formatted value if a field has a formatter function, and the field has its sortByFormatted
property is set to true
). The field value is first stringified if it is an object and then sorted.
Notes:
- The built-in
sort-compare
routine cannot sort based on the custom rendering of the field data: scoped slots are used only for presentation only, and do not affect the underlying data. - Fields that have a
formatter
function (virtual field or regular field) can be sorted by the value returned via the formatter function if the field propertysortByFormatted
is set totrue
. Optionally you can pass a formatter function reference tosortByFormatted
to format the value before sorting. The default isfalse
which will sort by the original field value. This is only applicable for the built-in sort-compare routine. - By default, the internal sorting routine will sort
null
,undefined
, or empty string values first (less than any other values). To sort so thatnull
,undefined
or empty string values appear last (greater than any other value), set thesort-null-last
prop totrue
.
For customizing the sort-compare handling, refer to the Custom sort-compare routine section below.
Internal sorting and locale handling
The internal sort-compare routine uses String.prototype.localeCompare()
for comparing the stringified column value (if values being compared are not both Number
or both Date
types). The browser native localeCompare()
method accepts a locale
string (or array of locale strings) and an options
object for controlling how strings are sorted. The default options are { numeric: true }
, and the locale is undefined
(which uses the browser default locale).
You can change the locale (or locales) via the sort-compare-locale
prop to set the locale(s) for sorting, as well as pass sort options via the sort-compare-options
prop.
The sort-compare-locale
prop defaults to undefined
, which uses the browser (or Node.js runtime) default locale. The prop sort-compare-locale
can either accept a BCP 47 language tag string or an array of such tags. For more details on locales, please see Locale identification and negotiation on MDN.
The sort-compare-options
prop accepts an object containing any of the following properties:
localeMatcher
: The locale matching algorithm to use. Possible values are'lookup'
and'best fit'
. The default is'best fit'
. For information about this option, see the MDN Intl page for details.sensitivity
: Which differences in the strings should lead to non-zero compare result values. Possible values are:'base'
: Only strings that differ in base letters compare as unequal. Examples:a ≠ b
,a = á
,a = A
.'accent'
: Only strings that differ in base letters or accents and other diacritic marks compare as unequal. Examples:a ≠ b
,a ≠ á
,a = A
.'case'
: Only strings that differ in base letters or case compare as unequal. Examples:a ≠ b
,a = á
,a ≠ A
.'variant'
: (default) Strings that differ in base letters, accents and other diacritic marks, or case compare as unequal. Other differences may also be taken into consideration. Examples:a ≠ b
,a ≠ á
,a ≠ A
.
ignorePunctuation
: Whether punctuation should be ignored. Possible values aretrue
andfalse
. The default isfalse
.numeric
: Whether numeric collation should be used, such that'1' < '2' < '10'
. Possible values aretrue
andfalse
. The default isfalse
. Note that implementations (browsers, runtimes) are not required to support this property, and therefore it might be ignored.caseFirst
: Whether upper case or lower case should sort first. Possible values are'upper'
,'lower'
, or'false'
(use the locale's default). The default is'false'
. Implementations are not required to support this property.'usage'
: Always set to'sort'
by<wui-table>
Example 1: If you want to sort German words, set sort-compare-locale="de"
(in German, ä
sorts before z
) or Swedish set sort-compare-locale="sv"
(in Swedish, ä
sorts after z
)
Example 2: To compare numbers that are strings numerically, and to ignore case and accents:
<wui-table
:sort-compare-options="{ numeric: true, sensitivity: 'base' }"
...
></wui-table>
Notes:
- Refer to MDN
String.prototype.localeCompare()
documentation for details on the options object property values. - Refer to MDN locales documentation for details on locale values.
- Not all browsers (or Node.js) support the
locale
andoptions
withString.prototype.localeCompare()
. Refer to Can I use for browser support. For Node.js, you may need to add in Intl support for handling locales, other than the default, to prevent SSR hydration mismatch errors.
Custom sort-compare routine
You can provide your own custom sort compare routine by passing a function reference to the prop sort-compare
. The sort-compare
routine is passed seven (7) arguments, of which the last 4 are optional:
- the first two arguments (
a
andb
) are the record objects for the rows being compared - the third argument is the field
key
being sorted on (sortBy
) - the fourth argument (
sortDesc
) is the order<wui-table>
will be displaying the records (true
for descending,false
for ascending) - the fifth argument is a reference to the field's formatter function or the field's
filterByFormatted
value if it is a function reference. If not formatter is found this value will beundefined
. You will need to call this method to get the formatted field value:valA = formatter(a[key], key, a)
andvalB = formatter(b[key], key, b)
, if you need to sort by the formatted value. This will beundefined
if the field'ssortByFormatted
property is nottrue
or is not a formatter function reference, or the fields formatter function cannot be found. - the sixth argument is the value of the
sort-compare-options
prop (default is{ numeric: true }
) - the seventh argument is the value of the
sort-compare-locale
prop (default isundefined
)
The sixth and seventh arguments can be used if you are using the String.prototype.localeCompare()
method to compare strings.
In most typical situations, you only need to use the first three arguments. The fourth argument - sorting direction - should not normally be used, as wui-table
will handle the direction, and this value is typically only needed when special handling of how null
and/or undefined
values are sorted (i.e. sorting null
/undefined
first or last).
The routine should return either -1
(or a negative value) for a[key] < b[key]
, 0
for a[key] === b[key]
, or 1
(or a positive value) for a[key] > b[key]
.
Your custom sort-compare routine can also return null
or false
, to fall back to the built-in sort-compare routine for the particular key
. You can use this feature (i.e. by returning null
) to have your custom sort-compare routine handle only certain fields (keys) such as the special case of virtual (scoped slot) columns, and have the internal (built in) sort-compare handle all other fields.
The default sort-compare routine works similar to the following. Note the fourth argument (sorting direction) is not used in the sort comparison:
function sortCompare(
aRow,
bRow,
key,
sortDesc,
formatter,
compareOptions,
compareLocale
) {
const a = aRow[key]; // or use Lodash `_.get()`
const b = bRow[key];
if (
(typeof a === "number" && typeof b === "number") ||
(a instanceof Date && b instanceof Date)
) {
// If both compared fields are native numbers or both are native dates
return a < b ? -1 : a > b ? 1 : 0;
} else {
// Otherwise stringify the field data and use String.prototype.localeCompare
return toString(a).localeCompare(
toString(b),
compareLocale,
compareOptions
);
}
}
// Helper function to stringify the values of an Object
function toString(value) {
if (value === null || typeof value === "undefined") {
return "";
} else if (value instanceof Object) {
return Object.keys(value)
.sort()
.map((key) => toString(value[key]))
.join(" ");
} else {
return String(value);
}
}
Disable local sorting
If you want to handle sorting entirely in your app, you can disable the local sorting in <wui-table>
by setting the prop no-local-sorting
to true
, while still maintaining the sortable header functionality (via sort-changed
or context-changed
events as well as syncable props).
You can use the syncable props sort-by.sync
and sort-desc.sync
to detect changes in sorting column and direction.
Also, When a sortable column header (or footer) is clicked, the event sort-changed
will be emitted with a single argument containing the context object of <wui-table>
. See the Detection of sorting change section below for details about the sort-changed event and the context object.
When no-local-sorting
is true
, the sort-compare
prop has no effect.
Change initial sort direction
Control the order in which ascending and descending sorting is applied when a sortable column header is clicked, by using the sort-direction
prop. The default value 'asc'
applies ascending sort first (when a column is not currently sorted). To reverse the behavior and sort in descending direction first, set it to 'desc'
.
If you don't want the current sorting direction to change when clicking another sortable column header, set sort-direction
to 'last'
. This will maintain the sorting direction of the previously sorted column.
For individual column initial sort direction (which applies when the column transitions from unsorted to sorted), specify the property sortDirection
in fields
. See the Complete Example below for an example of using this feature.
Filtering
Filtering, when used, is applied by default to the original items array data. wui-table
provides several options for how data is filtered.
It is currently not possible to filter based on result of formatting via scoped field slots.
Built in filtering
The item's row data values are stringified (see the sorting section above for how stringification is done) and the filter searches that stringified data (excluding any of the special properties that begin with an underscore '_'
). The stringification also, by default, includes any data not shown in the presented columns.
With the default built-in filter function, the filter
prop value can either be a string or a RegExp
object (regular expressions should not have the /g
global flag set).
If the stringified row contains the provided string value or matches the RegExp expression then it is included in the displayed results.
Set the filter
prop to null
or an empty string to clear the current filter.
Built in filtering options
There are several options for controlling what data the filter is applied against.
- The
filter-ignored-fields
prop accepts an array of top-level (immediate properties of the row data) field keys that should be ignored when filtering. - The
filter-included-fields
prop accepts an array of top-level (immediate properties of the row data) field keys that should used when filtering. All other field keys not included in this array will be ignored. This feature can be handy when you want to filter on specific columns. If the specified array is empty, then all fields are included, except those specified via the propfilter-ignored-fields
. If a field key is specified in bothfilter-ignored-fields
andfilter-included-fields
, thenfilter-included-fields
takes precedence. - Normally,
<wui-table>
filters based on the stringified record data. If the field has aformatter
function specified, you can optionally filter based on the result of the formatter by setting the field definition propertyfilterByFormatted
totrue
. If the field does not have a formatter function, this option is ignored. You can optionally pass a formatter function reference, to be used for filtering only, to the field definition propertyfilterByFormatted
.
The props filter-ignored-fields
and filter-included-fields
, and the field definition property filterByFormatted
have no effect when using a custom filter function, or items provider based filtering.
Custom filter function
You can also use a custom filter function, by setting the prop filter-function
to a reference of custom filter test function. The filter function will be passed two arguments:
- the original item row record data object. Treat this argument as read-only.
- the content of the
filter
prop (could be a string, RegExp, array, or object)
The function should return true
if the record matches your criteria or false
if the record is to be filtered out.
For proper reactive updates to the displayed data, when not filtering you should set the filter
prop to null
or an empty string (and not an empty object or array). The filter function will not be called when the filter
prop is a falsey value.
The display of the empty-filter-text
relies on the truthiness of the filter
prop.
Filter events
When local filtering is applied, and the resultant number of items change, <wui-table>
will emit the filtered
event with a two arguments:
- an array reference which is the complete list of items passing the filter routine. Treat this argument as read-only.
- the number of records that passed the filter test (the length of the first argument)
Setting the prop filter
to null or an empty string will clear local items filtering.
Debouncing filter criteria changes
deprecated
Use the debounce
feature of <wui-form-input>
instead.
If you have a text input tied to the filter
prop of <wui-table>
, the filtering process will occur for each character typed by the user. With large items datasets, this process can take a while and may cause the text input to appear sluggish.
To help alleviate this type of situation, <wui-table>
accepts a debounce timeout value (in milliseconds) via the filter-debounce
prop. The default is 0
(milliseconds). When a value greater than 0
is provided, the filter will wait for that time before updating the table results. If the value of the filter
prop changes before this timeout expires, the filtering will be once again delayed until the debounce timeout expires.
When used, the suggested value of filter-debounce
should be in the range of 100
to 200
milliseconds, but other values may be more suitable for your use case.
The use of this prop can be beneficial when using provider filtering with items provider functions, to help reduce the number of calls to your back end API.
Filtering notes
See the Complete Example below for an example of using the filter
feature.
Pagination
<wui-table>
supports built in pagination of item data. You can control how many rows are displayed at a time by setting the per-page
prop to the maximum number of rows you would like displayed, and use the current-page
prop to specify which page to display (starting from page 1
). If you set current-page
to a value larger than the computed number of pages, then no rows will be shown.
You can use the <wui-pagination>
component in conjunction with <wui-table>
for providing control over pagination.
Setting per-page
to 0
(default) will disable the local items pagination feature.
Using items provider functions
As mentioned under the Items prop section, it is possible to use a function to provide the row data (items), by specifying a function reference via the items
prop.
The provider function is called with the following signature:
provider(ctx, [callback]);
The ctx
is the context object associated with the table state, and contains the following properties:
Property | Type | Description |
---|---|---|
apiUrl | String | The value provided to the api-url prop. null if none provided. |
currentPage | Number | The current page number (starting from 1, the value of the current-page prop) |
filter | String or RegExp or Object | The value of the filter prop |
perPage | Number | The maximum number of rows per page to display (the value of the per-page prop) |
scrollBottomTriggerCounter | Number | The number of scroll-bottom events (only available when infinite prop is set, starting from 0 ). Can be used to calculate the offset for your API calls in the items provider function. Is reset to 0 if a filter or sort prop has changed |
sortBy | String | The current column key being sorted, or an empty string if not sorting |
sortDesc | Boolean | The current sort direction (true for descending, false for ascending) |
The second argument callback
is an optional parameter for when using the callback asynchronous method.
Example: returning an array of data (synchronous):
function myProvider() {
let items = [];
// Perform any items processing needed
// Must return an array
return items || [];
}
Example: Using callback to return data (asynchronous):
function myProvider(ctx, callback) {
const params = "?page=" + ctx.currentPage + "&size=" + ctx.perPage;
this.fetchData("/some/url" + params)
.then((data) => {
// Pluck the array of items off our axios response
const items = data.items;
// Provide the array of items to the callback
callback(items);
})
.catch(() => {
callback([]);
});
// Must return null or undefined to signal wui-table that callback is being used
return null;
}
Example: Using a Promise to return data (asynchronous):
function myProvider(ctx) {
const promise = axios.get(
"/some/url?page=" + ctx.currentPage + "&size=" + ctx.perPage
);
// Must return a promise that resolves to an array of items
return promise.then((data) => {
// Pluck the array of items off our axios response
const items = data.items;
// Must return an array of items or an empty array if an error occurred
return items || [];
});
}
Example: Using an async function (semi-synchronous):
Using an async method to return an items array is possible:
async function myProvider(ctx) {
try {
const response = await axios.get(
`/some/url?page=${ctx.currentPage}&size=${ctx.perPage}`
);
return response.items;
} catch (error) {
return [];
}
}
Example: Using an async function for infinite scrolling:
Using an async method and calculating the right offset for infinite scrolling with the help of the scrollBottomTriggerCounter
property:
async function myProvider(ctx) {
try {
const { filter, perPage, scrollBottomTriggerCounter } = ctx;
const offset = Math.max(
0,
perPage + scrollBottomTriggerCounter * perPage - perPage
);
const limit = parseInt(offset) + parseInt(perPage);
const response = await axios.get(
`/some/url?offset=${offset}&limit=${limit}`
);
return response.items;
} catch (error) {
return [];
}
}
Automated table busy state
<wui-table>
automatically tracks/controls its busy
state when items provider functions are used, however it also provides a busy
prop that can be used either to override the inner busy
state, or to monitor <wui-pagination>
's current busy state in your application using the 2-way .sync
modifier.
Note: in order to allow <wui-table>
fully track its busy
state, the custom items provider function should handle errors from data sources and return an empty array to <wui-table>
.
Example: usage of busy state
<template>
<div>
<wui-table
id="my-table"
:busy.sync="isBusy"
:items="myProvider"
:fields="fields"
...
></wui-table>
</div>
</template>
<script>
export default {
data () {
return {
isBusy: false
}
}
methods: {
myProvider () {
// Here we don't set isBusy prop, so busy state will be
// handled by table itself
// this.isBusy = true
let promise = axios.get('/some/url')
return promise.then((data) => {
const items = data.items
// Here we could override the busy state, setting isBusy to false
// this.isBusy = false
return(items)
}).catch(error => {
// Here we could override the busy state, setting isBusy to false
// this.isBusy = false
// Returning an empty array, allows table to correctly handle
// internal busy state in case of error
return []
})
}
}
}
</script>
If using an async/await
provider:
async function myProvider(ctx) {
this.isBusy = true;
try {
const response = await axios.get(
`/some/url?page=${ctx.currentPage}&size=${ctx.perPage}`
);
this.isBusy = false;
return response.items;
} catch (error) {
this.isBusy = false;
return [];
}
}
Notes:
- If you manually place the table in the
busy
state, the items provider will not be called/refreshed until thebusy
state has been set tofalse
. - All click related and hover events, and sort-changed events will not be emitted when in the
busy
state (either set automatically during provider update, or when manually set).
Provider paging, filtering, and sorting
By default, the items provider function is responsible for all paging, filtering, and sorting of the data, before passing it to wui-table
for display.
You can disable provider paging, filtering, and sorting (individually) by setting the following wui-table
prop(s) to true
:
Prop | Type | Default | Description |
---|---|---|---|
no-provider-paging | Boolean | false | When true enables the use of wui-table local data pagination |
no-provider-sorting | Boolean | false | When true enables the use of wui-table local sorting |
no-provider-filtering | Boolean | false | When true enables the use of wui-table local filtering |
When no-provider-paging
is false
(default), you should only return at maximum, perPage
number of records.
Notes:
<wui-table>
needs reference to your pagination and filtering values in order to trigger the calling of the provider function. So be sure to bind to theper-page
,current-page
andfilter
props onwui-table
to trigger the provider update function call (unless you have the respectiveno-provider-*
prop set totrue
).- The
no-local-sorting
prop has no effect whenitems
is a provider function. - When using provider filtering, you may find that setting the
debounce
prop on<wui-form-input>
to a value greater than100
ms will help minimize the number of calls to your back end API as the user types in the criteria.
Force refreshing of table data
You may also trigger the refresh of the provider function by emitting the event refresh::table
on $root
with the single argument being the id
of your wui-table
. You must have a unique ID on your table for this to work.
this.$root.$emit("wv::refresh::table", "my-table");
Or by calling the refresh()
method on the table reference
<div>
<wui-table ref="table" ...></wui-table>
</div>
this.$refs.table.refresh();
Note: If the table is in the busy
state (i.e. a provider update is currently running), the refresh will wait until the current update is completed. If there is currently a refresh pending and a new refresh is requested, then only one refresh will occur.
Detection of sorting change
By listening on <wui-table>
sort-changed
event, you can detect when the sorting key and direction have changed.
<div>
<wui-table @sort-changed="sortingChanged" ...></wui-table>
</div>
The sort-changed
event provides a single argument of the table's current state context object. This context object has the same format as used by items provider functions.
export default {
methods: {
sortingChanged(ctx) {
// ctx.sortBy ==> Field key for sorting by (or null for no sorting)
// ctx.sortDesc ==> true if sorting descending, false otherwise
},
},
};
You can also obtain the current sortBy and sortDesc values by using the :sort-by.sync
and :sort-desc.sync
two-way props respectively (see section Sorting above for details).
<div>
<wui-table
:sort-by.sync="mySortBy"
:sort-desc.sync="mySortDesc"
...
></wui-table>
</div>
Server side rendering
Special care must be taken when using server side rendering (SSR) and an items
provider function. Make sure you handle any special situations that may be needed server side when fetching your data!
When <wui-table>
is mounted in the document, it will automatically trigger a provider update call.
Light-weight tables
<wui-table-lite>
provides a great alternative to <wui-table>
if you just need simple display of tabular data. The <wui-table-lite>
component provides all of the styling and formatting features of <wui-table>
(including row details and stacked support), while excluding the following features:
- Filtering
- Sorting
- Pagination
- Items provider support
- Selectable rows
- Busy table state and styling
- Fixed top and bottom rows
- Empty row support
Table lite as a plugin
The TablePlugin
includes <wui-table-lite>
. For convenience, WuiVue also provides a TableLitePlugin
which installs only <wui-table-lite>
. TableLitePlugin
is available as a top level named export.
Simple tables
The <wui-table-simple>
component gives the user complete control over the rendering of the table content, while providing basic WUI table styling. <wui-table-simple>
is a wrapper component around the <table>
element. Inside the component, via the default
slot, you can use any or all of the WuiVue table helper components: <wui-thead>
, <wui-tfoot>
, <wui-tbody>
, <wui-tr>
, <wui-th>
, <wui-td>
, and the HTML5 elements <caption>
, <colgroup>
and <col>
. Contrary to the component's name, one can create simple or complex table layouts with <wui-table-simple>
.
<wui-table-simple>
provides basic styling options via props: striped
, bordered
, borderless
, outlined
, small
, hover
, dark
, fixed
, responsive
and sticky-header
. Note that stacked
mode is available but requires some additional markup to generate the cell headings, as described in the Simple tables and stacked mode section below. Sticky columns are also supported, but also require a bit of additional markup to specify which columns are to be sticky. See below for more information on using sticky columns.
Since wui-table-simple
is just a wrapper component, of which you will need to render content inside, it does not provide any of the advanced features of <wui-table>
(i.e. row events, head events, sorting, pagination, filtering, foot-clone, items, fields, etc.).
<div>
<wui-table-simple hover small caption-top responsive>
<caption>
Items sold in August, grouped by Country and City:
</caption>
<colgroup>
<col />
<col />
</colgroup>
<colgroup>
<col />
<col />
<col />
</colgroup>
<colgroup>
<col />
<col />
</colgroup>
<wui-thead head-variant="dark">
<wui-tr>
<wui-th colspan="2">Region</wui-th>
<wui-th colspan="3">Clothes</wui-th>
<wui-th colspan="2">Accessories</wui-th>
</wui-tr>
<wui-tr>
<wui-th>Country</wui-th>
<wui-th>City</wui-th>
<wui-th>Trousers</wui-th>
<wui-th>Skirts</wui-th>
<wui-th>Dresses</wui-th>
<wui-th>Bracelets</wui-th>
<wui-th>Rings</wui-th>
</wui-tr>
</wui-thead>
<wui-tbody>
<wui-tr>
<wui-th rowspan="3">Belgium</wui-th>
<wui-th class="text-right">Antwerp</wui-th>
<wui-td>56</wui-td>
<wui-td>22</wui-td>
<wui-td>43</wui-td>
<wui-td variant="success">72</wui-td>
<wui-td>23</wui-td>
</wui-tr>
<wui-tr>
<wui-th class="text-right">Gent</wui-th>
<wui-td>46</wui-td>
<wui-td variant="warning">18</wui-td>
<wui-td>50</wui-td>
<wui-td>61</wui-td>
<wui-td variant="danger">15</wui-td>
</wui-tr>
<wui-tr>
<wui-th class="text-right">Brussels</wui-th>
<wui-td>51</wui-td>
<wui-td>27</wui-td>
<wui-td>38</wui-td>
<wui-td>69</wui-td>
<wui-td>28</wui-td>
</wui-tr>
<wui-tr>
<wui-th rowspan="2">The Netherlands</wui-th>
<wui-th class="text-right">Amsterdam</wui-th>
<wui-td variant="success">89</wui-td>
<wui-td>34</wui-td>
<wui-td>69</wui-td>
<wui-td>85</wui-td>
<wui-td>38</wui-td>
</wui-tr>
<wui-tr>
<wui-th class="text-right">Utrecht</wui-th>
<wui-td>80</wui-td>
<wui-td variant="danger">12</wui-td>
<wui-td>43</wui-td>
<wui-td>36</wui-td>
<wui-td variant="warning">19</wui-td>
</wui-tr>
</wui-tbody>
<wui-tfoot>
<wui-tr>
<wui-td colspan="7" variant="secondary" class="text-right">
Total Rows: <b>5</b>
</wui-td>
</wui-tr>
</wui-tfoot>
</wui-table-simple>
</div>
<!-- wui-table-simple.vue -->
When in responsive
or sticky-header
mode, the <table>
element is wrapped inside a <div>
element. If you need to apply additional classes to the <table>
element, use the table-class
prop.
Any additional attributes given to <wui-table-simple>
will always be applied to the <table>
element.
Simple tables and stacked mode
A bit of additional markup is required on your <wui-table-simple>
body cells when the table is in stacked mode. Specifically, WuiVue uses a special data attribute to create the cell's heading, of which you can supply to <wui-td>
or <wui-th>
via the stacked-heading
prop. Only plain strings are supported (not HTML markup), as we use the pseudo element ::before
and CSS content
property.
Here is the same table as above, set to be always stacked, which has the extra markup to handle stacked mode (specifically for generating the cell headings):
<div>
<wui-table-simple hover small caption-top stacked>
<caption>
Items sold in August, grouped by Country and City:
</caption>
<colgroup>
<col />
<col />
</colgroup>
<colgroup>
<col />
<col />
<col />
</colgroup>
<colgroup>
<col />
<col />
</colgroup>
<wui-thead head-variant="dark">
<wui-tr>
<wui-th colspan="2">Region</wui-th>
<wui-th colspan="3">Clothes</wui-th>
<wui-th colspan="2">Accessories</wui-th>
</wui-tr>
<wui-tr>
<wui-th>Country</wui-th>
<wui-th>City</wui-th>
<wui-th>Trousers</wui-th>
<wui-th>Skirts</wui-th>
<wui-th>Dresses</wui-th>
<wui-th>Bracelets</wui-th>
<wui-th>Rings</wui-th>
</wui-tr>
</wui-thead>
<wui-tbody>
<wui-tr>
<wui-th rowspan="3" class="text-center">Belgium (3 Cities)</wui-th>
<wui-th stacked-heading="City" class="text-left">Antwerp</wui-th>
<wui-td stacked-heading="Clothes: Trousers">56</wui-td>
<wui-td stacked-heading="Clothes: Skirts">22</wui-td>
<wui-td stacked-heading="Clothes: Dresses">43</wui-td>
<wui-td stacked-heading="Accessories: Bracelets" variant="success"
>72</wui-td
>
<wui-td stacked-heading="Accessories: Rings">23</wui-td>
</wui-tr>
<wui-tr>
<wui-th stacked-heading="City">Gent</wui-th>
<wui-td stacked-heading="Clothes: Trousers">46</wui-td>
<wui-td stacked-heading="Clothes: Skirts" variant="warning">18</wui-td>
<wui-td stacked-heading="Clothes: Dresses">50</wui-td>
<wui-td stacked-heading="Accessories: Bracelets">61</wui-td>
<wui-td stacked-heading="Accessories: Rings" variant="danger"
>15</wui-td
>
</wui-tr>
<wui-tr>
<wui-th stacked-heading="City">Brussels</wui-th>
<wui-td stacked-heading="Clothes: Trousers">51</wui-td>
<wui-td stacked-heading="Clothes: Skirts">27</wui-td>
<wui-td stacked-heading="Clothes: Dresses">38</wui-td>
<wui-td stacked-heading="Accessories: Bracelets">69</wui-td>
<wui-td stacked-heading="Accessories: Rings">28</wui-td>
</wui-tr>
<wui-tr>
<wui-th rowspan="2" class="text-center"
>The Netherlands (2 Cities)</wui-th
>
<wui-th stacked-heading="City">Amsterdam</wui-th>
<wui-td stacked-heading="Clothes: Trousers" variant="success"
>89</wui-td
>
<wui-td stacked-heading="Clothes: Skirts">34</wui-td>
<wui-td stacked-heading="Clothes: Dresses">69</wui-td>
<wui-td stacked-heading="Accessories: Bracelets">85</wui-td>
<wui-td stacked-heading="Accessories: Rings">38</wui-td>
</wui-tr>
<wui-tr>
<wui-th stacked-heading="City">Utrecht</wui-th>
<wui-td stacked-heading="Clothes: Trousers">80</wui-td>
<wui-td stacked-heading="Clothes: Skirts" variant="danger">12</wui-td>
<wui-td stacked-heading="Clothes: Dresses">43</wui-td>
<wui-td stacked-heading="Accessories: Bracelets">36</wui-td>
<wui-td stacked-heading="Accessories: Rings" variant="warning"
>19</wui-td
>
</wui-tr>
</wui-tbody>
<wui-tfoot>
<wui-tr>
<wui-td colspan="7" variant="secondary" class="text-right">
Total Rows: <b>5</b>
</wui-td>
</wui-tr>
</wui-tfoot>
</wui-table-simple>
</div>
<!-- wui-table-simple-stacked.vue -->
Like <wui-table>
and <wui-table-lite>
, table headers (<thead>
) and footers (<tfoot>
) are visually hidden when the table is visually stacked. If you need a header or footer, you can do so by creating an extra <wui-tr>
inside of the <wui-tbody>
component (or in a second <wui-tbody>
component), and set a role of columnheader
on the child <wui-th>
cells, and use WUI responsive display utility classes to hide the extra row (or <wui-tbody>
) above a certain breakpoint when the table is no longer visually stacked (the breakpoint should match the stacked table breakpoint you have set), i.e. <wui-tr class="d-md-none">
would hide the row on medium and wider screens, while <wui-tbody class="d-md-none">
would hide the row group on medium and wider screens.
Note: stacked mode with <wui-table-simple>
requires that you use the WuiVue table helper components. Use of the regular <tbody>
, <tr>
, <td>
and <th>
element tags will not work as expected, nor will they automatically apply any of the required accessibility attributes.
Simple tables and sticky columns
Sticky columns are supported with <wui-table-simple>
, but you will need to set the sticky-column
prop on each table cell (in the thead
, tbody
, and tfoot
row groups) in the column that is to be sticky. For example:
<wui-table-simple responsive>
<wui-thead>
<wui-tr>
<wui-th sticky-column>Sticky Column Header</wui-th>
<wui-th>Heading 1</wui-th>
<wui-th>Heading 2</wui-th>
<wui-th>Heading 3</wui-th>
<wui-th>Heading 4</wui-th>
</wui-tr>
</wui-thead>
<wui-tbody>
<wui-tr>
<wui-th sticky-column>Sticky Column Row Header</wui-th>
<wui-td>Cell</wui-td>
<wui-td>Cell</wui-td>
<wui-td>Cell</wui-td>
<wui-td>Cell</wui-td>
</wui-tr>
<wui-tr>
<wui-th sticky-column>Sticky Column Row Header</wui-th>
<wui-td>Cell</wui-td>
<wui-td>Cell</wui-td>
<wui-td>Cell</wui-td>
<wui-td>Cell</wui-td>
</wui-tr>
</wui-tbody>
<wui-tfoot>
<wui-tr>
<wui-th sticky-column>Sticky Column Footer</wui-th>
<wui-th>Heading 1</wui-th>
<wui-th>Heading 2</wui-th>
<wui-th>Heading 3</wui-th>
<wui-th>Heading 4</wui-th>
</wui-tr>
</wui-tfoot>
</wui-table-simple>
As with <wui-table>
and <wui-table-lite>
, sticky columns are not supported when the stacked
prop is set on <wui-table-simple>
.
Table simple as a plugin
The TablePlugin
includes <wui-table-simple>
and all of the helper components. For convenience, WuiVue also provides a TableSimplePlugin
which installs <wui-table-simple>
and all of the helper components. TableSimplePlugin
is available as a top level named export.
Using table simple for custom layouts
Using <wui-table-simple>
can be useful to build custom layout because of it's flexibility.
If stickyHeader
prop is used, and set to desired height for table, we can have vertical scrolling inside table. It's also required for virtual scroller
component to work as expected. Also responsive
prop will enable table horizontal scroll if width of rows is greater then table width.
Each table cell needs a fixed width so that we can have UI where rows and columns will be in sync visually when scrolled, and it's easy for user to get better overview of table.
To achieve desired UI/UX for sticky header and footer element we need to use minimum amount of custom CSS together with wui utility classes.
In example below <wui-dynamic-scroller>
component is used instead of <wui-recycle-scroller>
because we want to have option to have "dynamic" row height but we need to provide some defaults for virtual scroll i.e. minItemSize
prop.
We can also use page-mode
prop in our case because we want our parent element (table) to contain scroll! That way we can keep header and footer sticky and have vertical and horizontal scroll inside table body. By using pageMode
prop virtual scroller will use parent scroll to compute which items are visible.
Below is an simplified example of above mentioned props and custom CSS where we can see main component structure. For full working example check Storybook page.
<template>
<wui-table-simple
table-class="qc-table display--flex flex-column"
sticky-header="800px"
responsive
borderless
>
<wui-thead class="qc-table__thead wui-bg-color--white">
<wui-tr class="qc-table__tr"> Sticky header cells... </wui-tr>
</wui-thead>
<wui-dynamic-scroller
:items="items"
:min-item-size="201"
:buffer="201"
list-class="qc-table__scroll_wrap"
item-class="qc-table__scroll_item"
page-mode
>
<template #default="{ item, index, active }">
<wui-dynamic-scroller-item
:item="item"
:active="active"
:data-index="index"
>
<wui-tbody class="qc-table__tbody">
<wui-tr class="qc-table__tr">
Virtual scrolling row cells...
</wui-tr>
</wui-tbody>
</wui-dynamic-scroller-item>
</template>
</wui-dynamic-scroller>
<wui-tfoot class="qc-table__tfoot">
<wui-tr class="qc-table__tr"> Sticky footer cells... </wui-tr>
</wui-tfoot>
</wui-table-simple>
</template>
<style scoped>
/* HINT: display: table; is used so that elements take 100% of content width */
div >>> .qc-table__thead {
display: table;
position: sticky;
top: 0;
z-index: 10;
border-bottom: 1px solid var(--wui-color-gray-87, #ddd);
}
/* HINT: Custom class used on virtual scroll wrapper element. It's used to force full width on virtual scroll to have horizontal scrolling */
div >>> .qc-table__scroll_wrap {
display: table;
overflow: auto;
}
div >>> .qc-table__tbody {
display: table;
border-bottom: 1px solid var(--wui-color-gray-87, #ddd);
}
div >>> .qc-table__tfoot {
display: table;
position: sticky;
bottom: 0;
z-index: 10;
}
div >>> .qc-table__tr {
display: flex;
}
div >>> .qc-table__cell {
display: flex;
align-items: center;
flex-shrink: 0;
padding: 5px 10px 5px 35px;
font-weight: normal;
}
div >>> .qc-table__cell--footer {
background: var(--wui-bg-color--highlight-20, #cde6e9);
}
div >>> .wui-bs-table--sticky-column {
position: sticky;
left: 0;
z-index: 10;
}
</style>
Table helper components
WuiVue provides additional helper child components when using <wui-table-simple>
, or the named slots top-row
, bottom-row
, thead-top
, and custom-foot
(all of which accept table child elements). The helper components are as follows:
wui-tbody
(<wui-table-simple>
only)wui-thead
(<wui-table-simple>
only)wui-tfoot
(<wui-table-simple>
only)wui-tr
wui-td
wui-th
These components are optimized to handle converting variants to the appropriate classes (such as handling table dark
mode), and automatically applying certain accessibility attributes (i.e. role
s and scope
s). They also can generate the stacked table, and sticky header and column, markup. Components <wui-table>
and <wui-table-lite>
use these helper components internally.
In the Simple tables example, we are using the helper components <wui-thead>
, <wui-tbody>
, <wui-tr>
, <wui-th>
, <wui-td>
and <wui-tfoot>
. While you can use regular table child elements (i.e. <tbody>
, <tr>
, <td>
, etc.) within <wui-table-simple>
, and the named slots top-row
, bottom-row
, and thead-top
, it is recommended to use these WuiVue table <wui-t*>
helper components. Note that there are no helper components for <caption>
, <colgroup>
or <col>
, so you may use these three HTML5 elements directly in <wui-table-simple>
.
- Table helper components
<wui-tr>
,<wui-td>
and<wui-th>
all accept avariant
prop, which will apply one of the WUI theme colors (custom theme colors are supported via theming.) and will automatically adjust to use the correct variant class based on the table'sdark
mode. - The helper components
<wui-thead>
,<wui-tfoot>
accept ahead-variant
andfoot-variant
prop respectively. Supported values are'dark'
,'light'
ornull
(null
uses the default table background). These variants also control the text color (light text for'dark'
variant, and dark text for the'light'
variant). - Accessibility attributes
role
andscope
are automatically set on<wui-th>
and<wui-td>
components based on their location (thead, tbody, or tfoot) and theirrowspan
orcolspan
props. You can override the automaticscope
androle
values by setting the appropriate attribute on the helper component. - For
<wui-tbody>
,<wui-thead>
, and<wui-tfoot>
helper components, the appropriate defaultrole
of'rowgroup'
will be applied, unless you override the role by supplying arole
attribute. - For the
<wui-tr>
helper component, the appropriate defaultrole
of'row'
will be applied, unless you override the role by supplying arole
attribute.<wui-tr>
does not add ascope
. - The
<wui-tbody>
element supports rendering a Vue<transition-group>
when either, or both, of thetbody-transition-props
andtbody-transition-handlers
props are used. See the Table body transition support section for more details.
Accessibility
The <wui-table>
and <wui-table-lite>
components, when using specific features, will attempt to provide the best accessibility markup possible.
When using <wui-table-simple>
with the helper table components, elements will have the appropriate roles applied by default, of which you can optionally override. When using click handlers on the <wui-table-simple>
helper components, you will need to apply appropriate aria-*
attributes, and set tabindex="0"
to make the click actions accessible to screen reader and keyboard-only users. You should also listen for @keydown.enter.prevent
to handle users pressing Enter to trigger your click on cells or rows (required for accessibility for keyboard-only users).
Heading accessibility
When a column (field) is sortable (<wui-table>
only) or there is a head-clicked
listener registered (<wui-table>
and <wui-table-lite>
), the header (and footer) <th>
cells will be placed into the document tab sequence (via tabindex="0"
) for accessibility by keyboard-only and screen reader users, so that the user may trigger a click (by pressing Enter on the header cells.
Data row accessibility
When the table is in selectable
mode (<wui-table>
only, and prop no-select-on-click
is not set), or if there is a row-clicked
event listener registered (<wui-table>
and <wui-table-lite>
), all data item rows (<tr>
elements) will be placed into the document tab sequence (via tabindex="0"
) to allow keyboard-only and screen reader users the ability to click the rows by pressing Enter or Space.
When the table items rows are placed in the document tab sequence (<wui-table>
and <wui-table-lite>
), they will also support basic keyboard navigation when focused:
- Down will move to the next row
- Up will move to the previous row
- End or Down+Shift will move to the last row
- Home or Up+Shift will move to the first row
- Enter or Space to click the row.
Row event accessibility
Note the following row based events/actions (available with <wui-table>
and <wui-table-lite>
) are not considered accessible, and should only be used if the functionality is non critical or can be provided via other means:
row-dblclicked
row-contextmenu
row-hovered
row-unhovered
row-middle-clicked
Note that the row-middle-clicked
event is not supported in all browsers (i.e. IE, Safari and most mobile browsers). When listening for row-middle-clicked
events originating on elements that do not support input or navigation, you will often want to explicitly prevent other default actions mapped to the down action of the middle mouse button. On Windows this is usually autoscroll, and on macOS and Linux this is usually clipboard paste. This can be done by preventing the default behaviour of the mousedown
or pointerdown
event.
Additionally, you may need to avoid opening a default system or browser context menu after a right click. Due to timing differences between operating systems, this too is not a preventable default behaviour of row-middle-clicked
. Instead, this can be done by preventing the default behaviour of the row-contextmenu
event.
It is recommended you test your app in as many browser and device variants as possible to ensure your app handles the various inconsistencies with events.
Complete example
<template>
<wui-container fluid>
<!-- User Interface controls -->
<wui-row>
<wui-col lg="6" class="m-y-5">
<wui-form-group
label="Sort"
label-for="sort-by-select"
label-cols-sm="3"
label-align-sm="right"
label-size="sm"
class="m-b-0"
v-slot="{ ariaDescribedby }"
>
<wui-input-group size="sm">
<wui-form-select
id="sort-by-select"
v-model="sortBy"
:options="sortOptions"
:aria-describedby="ariaDescribedby"
class="w-75"
>
<template #first>
<option value="">-- none --</option>
</template>
</wui-form-select>
<wui-form-select
v-model="sortDesc"
:disabled="!sortBy"
:aria-describedby="ariaDescribedby"
size="sm"
class="w-25"
>
<option :value="false">Asc</option>
<option :value="true">Desc</option>
</wui-form-select>
</wui-input-group>
</wui-form-group>
</wui-col>
<wui-col lg="6" class="m-y-5">
<wui-form-group
label="Initial sort"
label-for="initial-sort-select"
label-cols-sm="3"
label-align-sm="right"
label-size="sm"
class="m-b-0"
>
<wui-form-select
id="initial-sort-select"
v-model="sortDirection"
:options="['asc', 'desc', 'last']"
size="sm"
></wui-form-select>
</wui-form-group>
</wui-col>
<wui-col lg="6" class="m-y-5">
<wui-form-group
label="Filter"
label-for="filter-input"
label-cols-sm="3"
label-align-sm="right"
label-size="sm"
class="m-b-0"
>
<wui-input-group size="sm">
<wui-form-input
id="filter-input"
v-model="filter"
type="search"
placeholder="Type to Search"
></wui-form-input>
<wui-input-group-append>
<wui-button :disabled="!filter" @click="filter = ''"
>Clear</wui-button
>
</wui-input-group-append>
</wui-input-group>
</wui-form-group>
</wui-col>
<wui-col lg="6" class="m-y-5">
<wui-form-group
v-model="sortDirection"
label="Filter On"
description="Leave all unchecked to filter on all data"
label-cols-sm="3"
label-align-sm="right"
label-size="sm"
class="m-b-0"
v-slot="{ ariaDescribedby }"
>
<wui-form-checkbox-group
v-model="filterOn"
:aria-describedby="ariaDescribedby"
class="m-t-5"
>
<wui-form-checkbox value="name">Name</wui-form-checkbox>
<wui-form-checkbox value="age">Age</wui-form-checkbox>
<wui-form-checkbox value="isActive">Active</wui-form-checkbox>
</wui-form-checkbox-group>
</wui-form-group>
</wui-col>
<wui-col sm="5" md="6" class="m-y-5">
<wui-form-group
label="Per page"
label-for="per-page-select"
label-cols-sm="6"
label-cols-md="4"
label-cols-lg="3"
label-align-sm="right"
label-size="sm"
class="m-b-0"
>
<wui-form-select
id="per-page-select"
v-model="perPage"
:options="pageOptions"
size="sm"
></wui-form-select>
</wui-form-group>
</wui-col>
<wui-col sm="7" md="6" class="m-y-5">
<wui-pagination
v-model="currentPage"
:total-rows="totalRows"
:per-page="perPage"
align="fill"
size="sm"
class="m-y-0"
></wui-pagination>
</wui-col>
</wui-row>
<!-- Main table element -->
<wui-table
:items="items"
:fields="fields"
:current-page="currentPage"
:per-page="perPage"
:filter="filter"
:filter-included-fields="filterOn"
:sort-by.sync="sortBy"
:sort-desc.sync="sortDesc"
:sort-direction="sortDirection"
stacked="md"
show-empty
small
@filtered="onFiltered"
>
<template #cell(name)="row">
{ row.value.first } { row.value.last }
</template>
<template #cell(actions)="row">
<wui-button
size="sm"
@click="info(row.item, row.index, $event.target)"
class="m-r-5"
>
Info modal
</wui-button>
<wui-button size="sm" @click="row.toggleDetails">
{ row.detailsShowing ? 'Hide' : 'Show' } Details
</wui-button>
</template>
<template #row-details="row">
<wui-panel>
<ul>
<li v-for="(value, key) in row.item" :key="key">
{ key }: { value }
</li>
</ul>
</wui-panel>
</template>
</wui-table>
<!-- Info modal -->
<wui-modal
:id="infoModal.id"
:title="infoModal.title"
ok-only
@hide="resetInfoModal"
>
<pre>{ infoModal.content }</pre>
</wui-modal>
</wui-container>
</template>
<script>
export default {
data() {
return {
items: [
{
isActive: true,
age: 40,
name: { first: "Dickerson", last: "Macdonald" },
},
{ isActive: false, age: 21, name: { first: "Larsen", last: "Shaw" } },
{
isActive: false,
age: 9,
name: { first: "Mini", last: "Navarro" },
_rowVariant: "success",
},
{
isActive: false,
age: 89,
name: { first: "Geneva", last: "Wilson" },
},
{ isActive: true, age: 38, name: { first: "Jami", last: "Carney" } },
{
isActive: false,
age: 27,
name: { first: "Essie", last: "Dunlap" },
},
{
isActive: true,
age: 40,
name: { first: "Thor", last: "Macdonald" },
},
{
isActive: true,
age: 87,
name: { first: "Larsen", last: "Shaw" },
_cellVariants: { age: "danger", isActive: "warning" },
},
{
isActive: false,
age: 26,
name: { first: "Mitzi", last: "Navarro" },
},
{
isActive: false,
age: 22,
name: { first: "Genevieve", last: "Wilson" },
},
{ isActive: true, age: 38, name: { first: "John", last: "Carney" } },
{ isActive: false, age: 29, name: { first: "Dick", last: "Dunlap" } },
],
fields: [
{
key: "name",
label: "Person full name",
sortable: true,
sortDirection: "desc",
},
{
key: "age",
label: "Person age",
sortable: true,
class: "text-center",
},
{
key: "isActive",
label: "Is Active",
formatter: (value, key, item) => {
return value ? "Yes" : "No";
},
sortable: true,
sortByFormatted: true,
filterByFormatted: true,
},
{ key: "actions", label: "Actions" },
],
totalRows: 1,
currentPage: 1,
perPage: 5,
pageOptions: [5, 10, 15, { value: 100, text: "Show a lot" }],
sortBy: "",
sortDesc: false,
sortDirection: "asc",
filter: null,
filterOn: [],
infoModal: {
id: "info-modal",
title: "",
content: "",
},
};
},
computed: {
sortOptions() {
// Create an options list from our fields
return this.fields
.filter((f) => f.sortable)
.map((f) => {
return { text: f.label, value: f.key };
});
},
},
mounted() {
// Set the initial number of items
this.totalRows = this.items.length;
},
methods: {
info(item, index, button) {
this.infoModal.title = `Row index: ${index}`;
this.infoModal.content = JSON.stringify(item, null, 2);
this.$root.$emit("wv::show::modal", this.infoModal.id, button);
},
resetInfoModal() {
this.infoModal.title = "";
this.infoModal.content = "";
},
onFiltered(filteredItems) {
// Trigger pagination to update the number of buttons/pages due to filtering
this.totalRows = filteredItems.length;
this.currentPage = 1;
},
},
};
</script>
<!-- wui-table-complete.vue -->
Component reference
<wui-table>
Properties
Property | Type | Default | Description |
---|---|---|---|
api-url | String | Pass through prop. Passed as part of the context object sent to the items provider function | |
bordered | Boolean | false | Adds borders to all the cells and headers |
borderless | Boolean | false | Removes all borders from cells |
busy | Boolean | false | When set, forces the table into the busy state.Automatically set when an items provider function is being called |
caption | String | Text string to place in the caption element | |
caption-html Use with caution | String | HTML string to place in the caption element | |
caption-top | Boolean | false | Visually place the table caption above the table. Default is below |
current-page | Number or String | 1 | The current page number to display when the table is paginated. Starting from 1 and up |
dark | Boolean | false | Places the table in dark mode |
details-td-class | Array or Object or String | CSS class (or classes) to apply to the row details' td element for the row-details slot | |
empty-filtered-html Use with caution | String | HTML string to show when the table has no items to show due to filtering | |
empty-filtered-text | String | 'There are no records matching your request' | Text string to show when the table has no items to show due to filtering |
empty-html Use with caution | String | HTML string to show when the table has no items to show | |
empty-text | String | 'There are no records to show' | Text string to show when the table has no items to show |
fields | Array | null | Array of field names or array of field definition objects |
filter | Array or Object or String or RegExp | Criteria for filtering. Internal filtering supports only string or RegExpr criteria | |
filter-debounce | Number or String | 0 | Time in milliseconds to debounce changes to the filter criteria before filtering the records |
filter-function | Function | Reference to a function to use instead of the internal filtering function. Refer to the docs for details | |
filter-ignored-fields | Array | [] | Array of top level fields to ignore when filtering the item data |
filter-included-fields | Array | [] | Array of fields to include when filtering. Overrides filter-ignore-fields |
fixed | Boolean | false | Makes all columns equal width (fixed layout table). Will speed up rendering for large tables. Column widths can be set via CSS or colgroup |
foot-clone | Boolean | false | Enable to the footer of the table, and clone the header content by default |
foot-row-variant | String | Apply a WUI theme color variant to the tr element in the tfoot. Falls back to head-row-variant | |
foot-variant | String | Footer variant: 'light' or 'dark', or unset. May take precedence over foot-row-variant | |
head-row-variant | String | Apply a WUI theme color variant to the tr element in the thead | |
head-variant | String | Header variant: 'light' or 'dark', or unset. May take precedence over head-row-variant | |
hover | Boolean | true | Enables hover styling on rows |
id | String | Used to set the id attribute on the rendered content, and used as the base to generate any additional element IDs as needed | |
infinite | Boolean | false | Activates the infinite scrolling of the table. In this case an items provider function must be passed to the prop items . The table header automatically becomes sticky |
items | Array or Function | [] | Array of items to display, or an items provider function reference. Refer to the docs for details |
label-sort-asc | String | 'Click to sort Ascending' | Hidden string to place in the header cell when clicking the cell will change the sort direction to ascending |
label-sort-clear | String | 'Click to clear sorting' | Hidden string to place in the header cell when clicking the cell will clear the current sorting direction |
label-sort-desc | String | 'Click to sort Descending' | Hidden string to place in the header cell when clicking the cell will change the sort direction to descending |
no-border-collapse | Boolean | false | Disable's the collapsing of table borders. Useful when table has sticky headers or columns |
no-footer-sorting | Boolean | false | When the build in formatter is used, setting this prop will disable the sorting ability in the footer |
no-local-sorting | Boolean | false | Disabled the internal sorting routine, and expects the user to provde the items sorted. Sorting controls will still be available |
no-provider-filtering | Boolean | false | When set, uses internal filtering to pagination the data. Otherwise the provider is expected to perform the filtering |
no-provider-paging | Boolean | false | When set, uses internal paging to pagination the data. Otherwise the items provider is expected to perform the paging |
no-provider-sorting | Boolean | false | When set, uses internal sorting to sort the data. Otherwise the items provider is expected to perform the sorting |
no-select-on-click | Boolean | false | Disables row selection via click events. Row selection will be only available programmatically |
no-sort-reset | Boolean | false | When a table is sortable, clicking on any non-sortable column heading will clear the current sort values. Set this prop to disable this feature |
outlined | Boolean | false | Adds an outline border to the table element |
per-page | Number or String | 0 | Number of rows to show per page. Set to 0 to disable pagination |
primary-key | String | Name of a table field that contains a guaranteed unique value per row. Needed for tbody transition support, and also speeds up table rendering | |
responsive | Boolean or String | false | Makes the table responsive in width, adding a horizontal scrollbar. Set to true for always responsive or set to one of the breakpoints to switch from responsive to normal: 'sm', 'md', 'lg', 'xl' |
select-mode | String | 'multi' | The selectable mode for the table when 'selectable' is set. Possible values: 'single', 'multi' or 'range' |
selectable | Boolean | false | When set, places the table body rows in selectable mode |
selected-variant | String | 'active' | WUI color theme variant to set selected rows to. Use any of the standard WUI theme color variants, or the special table row variant 'active' (default). Set to an empty string to not use a variant |
show-empty | Boolean | false | When enabled, and there are no item records to show, shows a message that there are no rows to show |
small | Boolean | false | Renders the table with smaller cell padding |
sort-by | String | Field name that is currently being sorted. Set to null to clear sorting. Syncable with the .sync prop modifier | |
sort-compare | Function | A reference to a function for sort-comparing two rows of data. Defaults to the internal sort compare routine. See docs for details | |
sort-compare-locale | Array or String | The locale string (or array of locale string) that specified the language when sorting. See docs for details | |
sort-compare-options | Object | { 'numeric': true } | An object containing sort configuration for the 'String.prototype.sortLocale' method. See docs for details |
sort-desc | Boolean | false | Set to true to sort the column in descending order. Syncable with the .sync prop modifier |
sort-direction | String | 'asc' | The initial sorting direction to sort an unsorted column by: 'asc', 'desc', or 'last' (to use the previous sort direction) |
sort-icon-left | Boolean | false | Positions the sort control to the left of the header text. Default is on the right of the header text |
sort-null-last | Boolean | false | When sorting, null and undefined values will be sorted first (or last, depending on 'sort-desc'). Set this prop to sort null values last. Only applicable to internal sorting |
stacked | Boolean or String | false | Place the table in stacked mode. Set to true for always stacked, or set to one of the breakpoints to switch from stacked to normal: 'sm', 'md', 'lg', 'xl' |
sticky-header | Boolean or String | false | Makes the table header sticky. Set to true for a maximum height 300px tall table, or set to any valid CSS hight (including units) |
striped | Boolean | false | Applies striping to the tbody rows |
table-class | Array or Object or String | CSS class (or classes) to apply to the table element | |
table-variant | String | Apply a WUI theme color variant to the entire table | |
tbody-class | Array or Object or String | CSS class (or classes) to apply to the tbody element | |
tbody-style | Object | CSS styles for the tbody element | |
tbody-tr-attr | Object or Function | Attributes to be added to each tr in the tbody, or a function returning such attributes (see docs for details) | |
tbody-tr-class | Array or Object or String or Function | CSS class (or classes) to apply to the tr element in the tbody. Can be a function that returns a class (see docs for details) | |
tbody-transition-handlers | Object | Vue 'transition-group' event handlers. When provided will make the tbody a Vue 'transition-group' component | |
tbody-transition-props | Object | Vue 'transition-group' properties. When provided will make the tbody a Vue 'transition-group' component | |
tfoot-class | Array or Object or String | CSS class (or classes) to apply to the tfoot element | |
tfoot-tr-class | Array or Object or String | CSS class (or classes) to apply to the tr element in the tfoot | |
thead-cells-auto-size | Boolean | false | When set, it calculates and sets the CSS widths of each header cell depending on the size of the corresponding tbody cell. Useful in combination with display:block tbody's and thead > tr's |
thead-class | Array or Object or String | CSS class (or classes) to apply to the thead element | |
thead-tr-class | Array or Object or String | CSS class (or classes) to apply to the tr element in the thead | |
value v-model | Array | [] | Currently displayed row data. Read-only. Do not set a value on this prop |
:warning: Caution: Props that support HTML strings (
*-html
) can be vulnerable to Cross Site Scripting (XSS) attacks when passed raw user supplied values. You must properly sanitize the user input first!
v-model
Property | Event |
---|---|
value | input |
Slots
Name | Scope | Description |
---|---|---|
bottom-row | Yes (see package.json ) | Fixed bottom row slot for user supplied WUI-TD cells. Optionally Scoped |
cell() | Yes (see package.json ) | Default scoped slot for custom data rendering of field data. See docs for scoped data |
cell({key}) | Yes (see package.json ) | Scoped slot for custom data rendering of field data. '{key}' is the field's key name. See docs for scoped data |
custom-foot | Yes (see package.json ) | Custom footer content slot for user supplied WUI-TR, WUI-TH, WUI-TD. Optionally Scoped |
empty | Yes (see package.json ) | Content to display when no items are present in the items array. Optionally scoped |
emptyfiltered | Yes (see package.json ) | Content to display when no items are present in the filtered items array. Optionally scoped |
foot() | Yes (see package.json ) | Default scoped slot for custom rendering of field footer. See docs for scoped footer |
foot({key}) | Yes (see package.json ) | Scoped slot for custom rendering of field footer. '{key}' is the field's key name. See docs for scoped footer |
head() | Yes (see package.json ) | Default scoped slot for custom rendering of field header. See docs for scoped header |
head({key}) | Yes (see package.json ) | Scoped slot for custom rendering of field header. '{key}' is the field's key name. See docs for scoped header |
row-details | Yes (see package.json ) | Scoped slot for optional rendering additional record details. See docs for Row details support |
table-busy | No | Optional slot to place loading message when table is in the busy state |
table-caption | No | Content to display in the table's caption element |
table-colgroup | Yes (see package.json ) | Slot to place custom colgroup and col elements. Optionally scoped |
thead-top | Yes (see package.json ) | Slot above the column headers in the thead element for user-supplied WUI-TR's with WUI-TH/WUI-TD. Optionally scoped |
top-row | Yes (see package.json ) | Fixed top row slot for user supplied WUI-TD cells. Optionally scoped |
Events
Event | Arguments | Description |
---|---|---|
context-changed | ctx - Table state context object. See docs | Emitted whenever the table state context has changed |
filtered | filteredItems - Array of items after filtering (before local pagination occurs) | Emitted when local filtering causes a change in the number of items |
head-clicked | key - Column key clicked (field name)field - Field definition objectevent - Native event objectisFooter - 'True' if this event originated from clicking on the footer cell | Emitted when a header or footer cell is clicked. Not applicable for 'custom-foot' slot |
refreshed | Emitted when the items provider function has returned data | |
row-clicked | item - Item data of the row being clickedindex - Index of the row being clickedevent - Native event object | Emitted when a row is clicked |
row-contextmenu | item - Item data of the row being right clickedindex - Index of the row being right clickedevent - Native event object | Emitted when a row is right clicked |
row-dblclicked | item - Item data of the row being double clickedindex - Index of the row being double clickedevent - Native event object | Emitted when a row is double clicked |
row-hovered | item - Item data of the row being hoveredindex - Index of the row being hoveredevent - Native event object | Emitted when a row is hovered |
row-middle-clicked | item - Item data of the row being middle clickedindex - Index of the row being middle clickedevent - Native event object | Emitted when a row is middle clicked |
row-selected | rows - Array of the row items that are selected | Emitted when a row or rows have been selected or unselected |
row-unhovered | item - Item data of the row being unhoveredindex - Index of the row being unhoveredevent - Native event object | Emitted when a row is unhovered |
sort-changed | ctx - Table state context object. See docs | Emitted when the sorting on the table has changed |
$root
event listeners
You can control <wui-table>
by emitting the following events on $root
:
Event | Arguments | Description |
---|---|---|
wv::refresh::table | id - Table ID to refresh data | Refresh data of a specific table when this event is emitted on $root |
<wui-table-lite>
Properties
Property | Type | Default | Description |
---|---|---|---|
bordered | Boolean | false | Adds borders to all the cells and headers |
borderless | Boolean | false | Removes all borders from cells |
caption | String | Text string to place in the caption element | |
caption-html Use with caution | String | HTML string to place in the caption element | |
caption-top | Boolean | false | Visually place the table caption above the table. Default is below |
dark | Boolean | false | Places the table in dark mode |
details-td-class | Array or Object or String | CSS class (or classes) to apply to the td element in the details row | |
fields | Array | null | Array of field names or array of field definition objects |
fixed | Boolean | false | Makes all columns equal width (fixed layout table). Will speed up rendering for large tables. Column widths can be set via CSS or colgroup |
foot-clone | Boolean | false | Enable to the footer of the table, and clone the header content by default |
foot-row-variant | String | Apply a WUI theme color variant to the tr element in the tfoot. Falls back to head-row-variant | |
foot-variant | String | Footer variant: 'light' or 'dark', or unset. May take precedence over foot-row-variant | |
head-row-variant | String | Apply a WUI theme color variant to the tr element in the thead | |
head-variant | String | Header variant: 'light' or 'dark', or unset. May take precedence over head-row-variant | |
hover | Boolean | true | Enables hover styling on rows |
id | String | Used to set the id attribute on the rendered content, and used as the base to generate any additional element IDs as needed | |
items | Array | [] | Array of items to display |
no-border-collapse | Boolean | false | Disable's the collapsing of table borders. Useful when table has sticky headers or columns |
outlined | Boolean | false | Adds an outline border to the table element |
primary-key | String | Name of a table field that contains a guaranteed unique value per row. Needed for tbody transition support, and also speeds up table rendering | |
responsive | Boolean or String | false | Makes the table responsive in width, adding a horizontal scrollbar. Set to true for always responsive or set to one of the breakpoints to switch from responsive to normal: 'sm', 'md', 'lg', 'xl' |
small | Boolean | false | Renders the table with smaller cell padding |
stacked | Boolean or String | false | Place the table in stacked mode. Set to true for always stacked, or set to one of the breakpoints to switch from stacked to normal: 'sm', 'md', 'lg', 'xl' |
sticky-header | Boolean or String | false | Makes the table header sticky. Set to true for a maximum height 300px tall table, or set to any valid CSS hight (including units) |
striped | Boolean | false | Applies striping to the tbody rows |
table-class | Array or Object or String | CSS class (or classes) to apply to the table element | |
table-variant | String | Apply a WUI theme color variant to the entire table | |
tbody-class | Array or Object or String | CSS class (or classes) to apply to the tbody element | |
tbody-style | Object | CSS styles for the tbody element | |
tbody-tr-attr | Object or Function | Attributes to be added to each tr in the tbody, or a function returning such attributes (see docs for details) | |
tbody-tr-class | Array or Object or String or Function | CSS class (or classes) to apply to the tr element in the tbody. Can be a function that returns a class (see docs for details) | |
tbody-transition-handlers | Object | Vue 'transition-group' event handlers. When provided will make the tbody a Vue 'transition-group' component | |
tbody-transition-props | Object | Vue 'transition-group' properties. When provided will make the tbody a Vue 'transition-group' component | |
tfoot-class | Array or Object or String | CSS class (or classes) to apply to the tfoot element | |
tfoot-tr-class | Array or Object or String | CSS class (or classes) to apply to the tr element in the tfoot | |
thead-cells-auto-size | Boolean | false | When set, it calculates and sets the CSS widths of each header cell depending on the size of the corresponding tbody cell. Useful in combination with display:block tbody's and thead > tr's |
thead-class | Array or Object or String | CSS class (or classes) to apply to the thead element | |
thead-tr-class | Array or Object or String | CSS class (or classes) to apply to the tr element in the thead | |
value v-model | Array | [] | Currently displayed row data. Read-only. Do not set a value on this prop |
:warning: Caution: Props that support HTML strings (
*-html
) can be vulnerable to Cross Site Scripting (XSS) attacks when passed raw user supplied values. You must properly sanitize the user input first!
v-model
Property | Event |
---|---|
value | input |
Slots
Name | Scope | Description |
---|---|---|
cell() | Yes (see package.json ) | Default scoped slot for custom data rendering of field data |
cell({key}) | Yes (see package.json ) | Scoped slot for custom data rendering of field data. '{key}' is the field's key name |
custom-foot | Yes (see package.json ) | Custom footer content slot for user supplied WUI-TR's with WUI-TH/WUI-TD. Optionally Scoped |
foot() | Yes (see package.json ) | Default scoped slot for custom rendering of field footer |
foot({key}) | Yes (see package.json ) | Scoped slot for custom rendering of field footer. '{key}' is the field's key name |
head() | Yes (see package.json ) | Default scoped slot for custom rendering of field header |
head({key}) | Yes (see package.json ) | Scoped slot for custom rendering of field header. '{key}' is the field's key name |
row-details | Yes (see package.json ) | Scoped slot for optional rendering additional record details. See docs for Row details support |
table-caption | No | Content to display in the table's caption element |
table-colgroup | Yes (see package.json ) | Slot to place custom colgroup and col elements. Optionally scoped |
thead-top | Yes (see package.json ) | Slot above the column headers in the thead element for user-supplied WUI-TR with WUI-TH/WUI-TD. Optionally scoped |
Events
Event | Arguments | Description |
---|---|---|
head-clicked | key - Column key clicked (field name)field - Field definition objectevent - Native event objectisFooter - 'True' if this event originated from clicking on the footer cell | Emitted when a header or footer cell is clicked. Not applicable for 'custom-foot' slot |
row-clicked | item - Item data of the row being clickedindex - Index of the row being clickedevent - Native event object | Emitted when a row is clicked |
row-contextmenu | item - Item data of the row being right clickedindex - Index of the row being right clickedevent - Native event object | Emitted when a row is right clicked |
row-dblclicked | item - Item data of the row being double clickedindex - Index of the row being double clickedevent - Native event object | Emitted when a row is double clicked |
row-hovered | item - Item data of the row being hoveredindex - Index of the row being hoveredevent - Native event object | Emitted when a row is hovered |
row-middle-clicked | item - Item data of the row being middle clickedindex - Index of the row being middle clickedevent - Native event object | Emitted when a row is middle clicked |
row-unhovered | item - Item data of the row being unhoveredindex - Index of the row being unhoveredevent - Native event object | Emitted when a row is unhovered |
<wui-table-simple>
Properties
Property | Type | Default | Description |
---|---|---|---|
bordered | Boolean | false | Adds borders to all the cells and headers |
borderless | Boolean | false | Removes all borders from cells |
caption-top | Boolean | false | Visually place the table caption above the table. Default is below |
dark | Boolean | false | Places the table in dark mode |
fixed | Boolean | false | Makes all columns equal width (fixed layout table). Will speed up rendering for large tables. Column widths can be set via CSS or colgroup |
hover | Boolean | true | Enables hover styling on rows |
id | String | Used to set the id attribute on the rendered content, and used as the base to generate any additional element IDs as needed | |
no-border-collapse | Boolean | false | Disable's the collapsing of table borders. Useful when table has sticky headers or columns |
outlined | Boolean | false | Adds an outline border to the table element |
responsive | Boolean or String | false | Makes the table responsive in width, adding a horizontal scrollbar. Set to true for always responsive or set to one of the breakpoints to switch from responsive to normal: 'sm', 'md', 'lg', 'xl' |
small | Boolean | false | Renders the table with smaller cell padding |
stacked | Boolean or String | false | Place the table in stacked mode. Set to true for always stacked, or set to one of the breakpoints to switch from stacked to normal: 'sm', 'md', 'lg', 'xl' |
sticky-header | Boolean or String | false | Makes the table header sticky. Set to true for a maximum height 300px tall table, or set to any valid CSS hight (including units) |
striped | Boolean | false | Applies striping to the tbody rows |
table-class | Array or Object or String | CSS class (or classes) to apply to the table element | |
table-variant | String | Apply a WUI theme color variant to the entire table |
Slots
Name | Description |
---|---|
default | Content to place in the table |
<wui-tbody>
Properties
Property | Type | Default | Description |
---|---|---|---|
tbody-style | Object | CSS styles for the tbody element | |
tbody-transition-handlers | Object | Vue 'transition-group' event handlers. When provided will make the tbody a Vue 'transition-group' component | |
tbody-transition-props | Object | Vue 'transition-group' properties. When provided will make the tbody a Vue 'transition-group' component |
Slots
Name | Description |
---|---|
default | Content to place in the tbody |
<wui-thead>
Properties
Property | Type | Default | Description |
---|---|---|---|
head-variant | String | Header variant: 'light' or 'dark', or unset | |
thead-cells-auto-size | Boolean | false | When set, it calculates and sets the CSS widths of each header cell depending on the size of the corresponding tbody cell. Useful in combination with display:block tbody's and thead > tr's |
Slots
Name | Description |
---|---|
default | Content to place in the thead |
<wui-tfoot>
Properties
Property | Type | Default | Description |
---|---|---|---|
foot-variant | String | Footer variant: 'light' or 'dark', or unset |
Slots
Name | Description |
---|---|
default | Content to place in the tfoot |
<wui-tr>
Properties
Property | Type | Default | Description |
---|---|---|---|
variant | String | Applies one of the WUI theme color variants to the component |
Slots
Name | Description |
---|---|
default | Content to place in the tr |
<wui-td>
Properties
Property | Type | Default | Description |
---|---|---|---|
colspan | Number or String | null | Number of columns this cell spans |
rowspan | Number or String | null | Number of rows this cell spans |
stacked-heading | String | Heading for the cell when in stacked mode. Only applicable to cells in the 'tbody' element | |
sticky-column | Boolean | false | If this will be a sticky colum. Must be set on all cells in this column. table must be in sticky-header or responsive mode to work |
variant | String | Applies one of the WUI theme color variants to the component |
Slots
Name | Description |
---|---|
default | Content to place in the td |
<wui-th>
Properties
Property | Type | Default | Description |
---|---|---|---|
colspan | Number or String | null | Number of columns this cell spans |
rowspan | Number or String | null | Number of rows this cell spans |
stacked-heading | String | Heading for the cell when in stacked mode. Only applicable to cells in the 'tbody' element | |
sticky-column | Boolean | false | If this will be a sticky colum. Must be set on all cells in this column. table must be in sticky-header or responsive mode to work |
variant | String | Applies one of the WUI theme color variants to the component |
Slots
Name | Description |
---|---|
default | Content to place in the th |
Importing individual components
You can import individual components into your project via the following named exports:
Component | Named Export |
---|---|
<wui-table> | WuiTable |
<wui-table-lite> | WuiTableLite |
<wui-table-simple> | WuiTableSimple |
<wui-tbody> | WuiTbody |
<wui-thead> | WuiThead |
<wui-tfoot> | WuiTfoot |
<wui-tr> | WuiTr |
<wui-td> | WuiTd |
<wui-th> | WuiTh |
Example
import { WuiTable } from "@wui/wui-vue/lib/table";
Vue.component("wui-table", WuiTable);
Importing as a Vue.js plugin
This plugin includes all of the above listed individual components.
Named Export | Import Path |
---|---|
TablePlugin | wui/wui-vue/lib/table |
This plugin also automatically includes the following plugins:
TableLitePlugin
TableSimplePlugin
Example
import { TablePlugin } from "@wui/wui-vue/lib/table";
Vue.use(TablePlugin);