Data Table
QTable is a Component that allows you to display data in a tabular manner. Features:
- Filtering
- Sorting
- Single / Multiple rows selection with custom selection actions
- Pagination (including server-side if required)
- Total customization of rows and cells through scoped slots
- Ability to add additional row(s) at top or bottom of data rows
- Column picker (through QTableColumns component described in one of the sections)
- Custom top and/or bottom Table controls
- Responsive design (“dense” mode for narrow windows)
Installation
Edit /quasar.conf.js
:framework: {
components: [
'QTable',
// pick only what you are using from:
'QTh',
'QTr',
'QTd',
'QTableColumns'
]
}
Basic Usage
This is the most basic QTable:<template>
<q-table
title="Table Title"
:data="tableData"
:columns="columns"
row-key="name"
/>
</template>
<script>
export default {
data: () => ({
columns: [
{
name: 'desc',
required: true,
label: 'Dessert (100g serving)',
align: 'left',
field: 'name',
sortable: true,
classes: 'my-class',
style: 'width: 500px'
},
...
],
tableData: [
{
name: 'Frozen Yogurt',
calories: 159,
fat: 6.0,
carbs: 24,
protein: 4.0,
sodium: 87,
calcium: '14%',
iron: '1%'
},
...
]
})
}
</script>
Internationalization
The default values of the different QTable labels are taken care of by default through Quasar I18n. If your desired language pack is missing, please provide a PR for it.
QTable Vue Properties
Vue Property | Type | Description |
---|---|---|
grid | Boolean | (v0.17+) Use “grid mode”. See example below. |
data | Array of Objects | Data containing Array of rows to display. |
columns | Array of Objects | (Required) Defining each column’s properties. |
row-key | String | (Required) Property name of each row defining a unique data key for the respective rows. |
pagination | Object | Use with .sync. Control of the pagination and sorting. Can enable Table “server-mode” by containing rowsNumber property. See next sections for details. |
rows-per-page-options | Array | Array of Numbers representing options for user to select how many rows per page should be shown. Example: ‘[3, 5, 7, 0]’. Notice value 0 means “All” and empty array hides the selection. |
selection | String | Set selection mode. One of ‘single’, ‘multiple’ or (default) ‘none’. |
selected | Array | Use with .sync. Array of unique keys for selected row(s). |
visible-columns | Array | Array of Strings containing the ‘name’ column property value of the visible columns. |
loading | Boolean | Show a background process is in progress (like fetching data and so on). |
color | String | Color of the default Table controls (pagination, checkboxes, …). |
dark | Boolean | When using Table on a dark background. |
dense | Boolean | Dense Table, when you want to display more data using the same real estate on window. Gets activated by default on narrow windows. |
title | String | Title of Table. |
hide-header | Boolean | Hide Table header. |
hide-bottom | Boolean | Hide Table bottom (usually containing pagination controls). |
separator | String | Sets separator for rows/columns/cell. One of ‘horizontal’, ‘vertical’, ‘cell’, ‘none’. |
table-style | String/Array/Object | Style for the <table> tag itself. |
table-class | String/Array/Object | Classes for the <table> tag itself. |
filter | String | Filter String for Table used by filter-method() . |
filter-method | Function | When you want a custom filtering method. See next sections for details. |
sort-method | Function | When you want a custom sorting method. See next sections for details. |
binary-state-sort | Boolean | (v0.17.11+) By default, sorting a column has 3 states (no sort, ascending, descending). By using this property it only allows 2 states (ascending, descending). |
Label properties are by default defined in Quasar’s i18n, but you can override them:
Vue Property | Type | Description |
---|---|---|
no-data-label | String | Message to display when no rows are available. |
no-results-label | String | Message to display when no rows match the filter. |
loading-label | String | Message to display when Table currently has no rows but is in the process of fetching them. |
selected-rows-label(rowsNumber) | Function | Function that returns a message (String) to display how many rows are selected. Takes a Number parameter which is the actual rows number that are selected. |
rows-per-page-label | String | Override ‘Rows per page:’ message. |
pagination-label(start,end,total) | Function | Override default ‘x-y of z’ pagination label. |
IMPORTANT
Initial sorted column, sorting direction & page is configured through thepagination
prop. Check the Pagination section below.
QTableColumns Vue Properties
Supports v-model
which should be the String for single selection and Array for multiple selection.
Vue Property | Type | Description |
---|---|---|
columns | Array of Objects | (Required) Defining each column’s properties. |
label | String | Overrides text displayed in input frame. See “Working with Display Value” section below. |
color | String | One from Quasar Color Palette. |
dark | Boolean | Is it rendered on a dark background? |
Defining the Columns
Let’s take an example of configuring the columns
property. Let’s assume we are telling QTable that row-key is ‘name’.columns: /* array of Objects */ [
// column Object definition
{
// unique id (used by row-key, pagination.sortBy, ...)
name: 'desc',
// label for header
label: 'Dessert (100g serving)',
// row Object property to determine value for this column
field: 'name',
// OR field: row => row.some.nested.prop
// (optional) if we use visible-columns, this col will always be visible
required: true,
// (optional) alignment
align: 'left',
// (optional) tell QTable you want this column sortable
sortable: true,
// (optional) compare function if you have
// some custom data or want a specific way to compare two rows
sort: (a, b) => parseInt(a, 10) - parseInt(b, 10)
// function return value:
// * is less than 0 then sort a to an index lower than b, i.e. a comes first
// * is 0 then leave a and b unchanged with respect to each other, but sorted with respect to all different elements
// * is greater than 0 then sort b to an index lower than a, i.e. b comes first
// (optional) you can format the data with a function
format: val => `${val}%`
// v0.17.9+; if using scoped slots, apply this yourself instead
style: 'width: 500px',
classes: 'my-special-class'
},
{ name: 'calories', label: 'Calories', field: 'calories', sortable: true },
{ name: 'fat', label: 'Fat (g)', field: 'fat', sortable: true },
{ name: 'carbs', label: 'Carbs (g)', field: 'carbs' },
{ name: 'protein', label: 'Protein (g)', field: 'protein' },
{ name: 'sodium', label: 'Sodium (mg)', field: 'sodium' },
{ name: 'calcium', label: 'Calcium (%)', field: 'calcium', sortable: true, sort: (a, b) => parseInt(a, 10) - parseInt(b, 10) },
{ name: 'iron', label: 'Iron (%)', field: 'iron', sortable: true, sort: (a, b) => parseInt(a, 10) - parseInt(b, 10) }
]
IMPORTANT
If your data is nested and you need to fill a column with a certain value in the nested objects, you can grab a property value within the nested objects like so:field: obj => obj.some.nested.prop
Pagination
When you want to control Table’s pagination, use pagination
prop, but don’t forget to add the .sync
modifier:<template>
<div>
<q-table :pagination.sync="pagination" ... />
<q-btn @click="pagination.page++" label="Next page" ... />
</div>
</template>
<script>
export default {
data: () => ({
pagination: {
sortBy: null, // String, column "name" property value
descending: false,
page: 1,
rowsPerPage: 5 // current rows per page being displayed
}
})
}
When pagination has a property named
rowsNumber
, then this means that you’ll be configuring Table for server-side pagination (& sorting & filtering).
Custom Filter Method
<template> |
Custom Sort Method
<template> |
Popup Edit
Quasar v0.17.10+
Below is an example with the user being able to edit “in place” with the help of QPopupEdit component. Please note that we are using the “body” scoped slot. QPopupEdit won’t work with cell scoped slots.<q-table
:data="data"
:columns="columns"
:filter="filter"
:title="title"
row-key="name"
>
<q-tr slot="body" slot-scope="props" :props="props">
<q-td key="desc" :props="props">
{{ props.row.name }}
<q-popup-edit v-model="props.row.name">
<q-field count>
<q-input v-model="props.row.name" />
</q-field>
</q-popup-edit>
</q-td>
<q-td key="calories" :props="props">
{{ props.row.calories }}
<q-popup-edit v-model="props.row.calories" title="Update calories" buttons>
<q-input type="number" v-model="props.row.calories" />
</q-popup-edit>
</q-td>
<q-td key="fat" :props="props">{{ props.row.fat }}</q-td>
<q-td key="carbs" :props="props">{{ props.row.carbs }}</q-td>
<q-td key="protein" :props="props">{{ props.row.protein }}</q-td>
<q-td key="sodium" :props="props">{{ props.row.sodium }}</q-td>
<q-td key="calcium" :props="props">{{ props.row.calcium }}</q-td>
<q-td key="iron" :props="props">{{ props.row.iron }}</q-td>
</q-tr>
</q-table>
QTable Vue Events
Vue Event | Parameters | Description |
---|---|---|
@request | Object { pagination, filter, getCellValue } | Gets triggered when using server-side pagination (pagination property Object contains rowsNumber ) |
@fullscreen | Boolean (true /false ) | (v0.17.7+) Emitted when Fullscreen state gets toggled. |
Server-side Pagination, Filtering, Sorting
When your database contains a big number of rows for a Table, obviously it’s not feasible to load them all for multiple reasons (memory, UI rendering performance, …). Instead, you can load only a Table page. Whenever the user wants to navigate to another Table page, or wants to sort by a column or wants to filter the Table, a request is sent to the server to fetch the partial data.
First step to enable this behavior is to specify
pagination
prop, which MUST containrowsNumber
. QTable needs to know the total number of rows available in order to correctly render the pagination links.Second step is to listen for
@request
event on QTable. This event is triggered when data needs to be fetched from the server because either page number or sorting or filtering changed.It’s best that you also specify the
loading
prop in order to notify the user that a background process is in progress.
<template> |
Examples - Features
Filter, Column selection, Separators, Toggle Fullscreen
<template> |
Grid Mode
Requires Quasar v0.17+
Notice we’ll be using a Vue scoped slot called item
to define how each record (the equivalent of a row in non-grid mode) should look. This allows you total freedom.
The code below is the equivalent of the demo. Feel free to tweak however you want as all the QTable features are available in grid mode too. In the example below, we hide the header, but you can show it should you want – the user will be able to sort the data by columns etc.<q-table
grid
hide-header
:data="data"
:columns="columns"
:filter="filter"
:selection="selection"
:selected.sync="selected"
:visible-columns="visibleColumns"
row-key="name"
>
<template slot="top-right" slot-scope="props">
<q-search hide-underline v-model="filter" />
</template>
<div
slot="item"
slot-scope="props"
class="q-pa-xs col-xs-12 col-sm-6 col-md-4 col-lg-3 transition-generic"
:style="props.selected ? 'transform: scale(0.95);' : ''"
>
<q-card class="transition-generic" :class="props.selected ? 'bg-grey-2' : ''">
<q-card-title class="relative-position">
<q-checkbox v-model="props.selected" :label="props.row.name" />
</q-card-title>
<q-card-separator />
<q-card-main class="q-pa-none">
<q-list no-border>
<q-item v-for="col in props.cols.filter(col => col.name !== 'desc')" :key="col.name">
<q-item-main>
<q-item-tile label>{{ col.label }}</q-item-tile>
</q-item-main>
<q-item-side right>
<q-item-tile>{{ col.value }}</q-item-tile>
</q-item-side>
</q-item>
</q-list>
</q-card-main>
</q-card>
</div>
</q-table>
Row selection, Extra top/bottom rows, Loading state
<template> |
Controlling pagination, custom controls & watching for page navigation
<template> |
Row selection actions
<q-table |
Hide header & bottom
<q-table |
Display a nested property or format a column
You can display the value of a nested property. For example:columns: [
{
name: 'author',
label: 'Author',
field: row => row.author.name
}
]
Then you can go even further and format the value for a specific column in your column definition. Example:columns: [
{
name: 'author',
label: 'Author',
field: row => row.author.name,
format: val => `${val}%`
}
]
The value returned by field
is used for sorting rows, while the format
value is specifically meant for displaying a value to the user. This is very useful for cases where you need to sort by the initial value of your data. You can (if you want to), however, avoid the format
and use custom scoped slots (row, column cell) for defining how Quasar should format the cell(s).
Examples - Customization
Custom table top & bottom
<q-table |
Custom column cell
<q-table |
Custom rows
<q-table |
Alternative custom rows
<q-table |
Custom header (has tooltips)
<q-table |
Alternative custom header
<q-table |
Custom header & rows with selection & expandable rows
<q-table |