Tables
DataTable Vue Components with a Bulma implementation over a renderless core
Can be used outside of the Enso ecosystem.
For live examples and demos, you may visit laravel-enso.com
Should be used with its backend sibling
Installation
Install the package:
yarn add @enso-ui/tables @enso-ui/toastr
(within Enso, remember to cd
into the client
folder before installing front-end assets)
Note that this package has a couple of external dependencies. Read here for more info.
Exports
@enso-ui/tables/bulma
:
EnsoTable
,VueTable
,
@enso-ui/tables/renderless
:
CoreTable
,
Usage
Outside of Enso
Import the following in a main js file:
import Vue from 'vue';
import axios from 'axios';
import Toastr from '@enso-ui/toastr/bulma';
import ToastrPlugin from '@enso-ui/toastr';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
Vue.component('fa', FontAwesomeIcon);
Vue.use(ToastrPlugin, {
layout: Toastr,
options: {
duration: 3500,
position: 'right',
},
});
window.axios = axios;
Then in any given page import the component:
import { VueTable } from '@enso-ui/tables/bulma';
And then use it within your page:
<vue-table class="box is-paddingless raises-on-hover is-rounded"
path="/examples/table/init"
id="example"/>
Within Enso
Enso comes with a series of defaults and conventions, and there are Enso specific versions of some components that make use of these conventions so you add as little as possible configuration.
Import the Enso version of the component:
import { EnsoTable } from '@enso-ui/tables/bulma';
Use the component in your page:
<enso-table class="box is-paddingless raises-on-hover"
id="permissions"/>
If you're following conventions, the table component will try to determine
the endpoint's path by using the current page's path as well as assuming
the path's suffix will be initTable
.
Of course, you can still manually specify and thus overwrite this determined path:
<enso-table class="box is-paddingless raises-on-hover"
path="/api/system/permissions/initTable"
id="permissions"/>
There's also the route()
helper available, so instead of the above you can also use
it in combination with named routes:
:path="route('system.permissions.initTable')"
Usage Details
CoreTable
This is the renderless version of the component that can be built upon to create your own custom version.
Props
errorHandler
-function
- used to handle axios errorsfilters
-object
- a filters object that is sent to the back-end to filter the resultsid
-string
, required, an identifier for the table component, required for caching this table's preferences/settingsintervals
-object
, optional - an interval filters object that is sent to the back-end to filter the results. Note that if the interval is a date, in addition to themin
andmax
values for a given interval, you must also give thedateFormat
parameter, with the date format to be used for exampleintervals
can look like this:data() { return { intervals: { menus: { updated_at: { min: null, max: null, dateFormat: 'Y-m-d H-i-s', }, }, }, }; },
If using within Enso, you may forgo specifying a format, and can set the
dateFormat
attribute asnull
, in which case, the back-end component will use the global Enso format, from the configuration file (config('enso.config.dateTimeFormat')
).Note that even if null, you're still required to specify the
dateFormat
attribute for date intervals.i18n
,function
, optional - the function that handles localisationparams
-object
, optional - a params object that may be used on the back-end to perform additional, custom, operationsinitParams
-object
, optional - a params object that may be used on the back-end during the init phasepath
-string
, required - the URI path used to fetch the table template.
Events:
ready
- when the component is initialized after fetching its template, BEFORE fetching the actual table datafetching
- just before making the request to fetch the table datafetched
- when the table data has been fetchedclicked
- when clicking on aclickable
columnreset
- when clicking the reset button
Various other user events may be emitted depending on the configured template.
VueTable
This is the main bulma styled component and it is built upon the renderless
component renderless/VueTable.vue
.
Example:
<vue-table :path="route('system.menus.initTable')"
:error-handler="myErrorHandler"
:intervals="intervals"
:i18n="myI18n"
id="myTable"/>
Props
All the props from CoreTable
can be provided here
Slots
- if any columns have the
slot
attribute in their meta, in the template, then a scoped slot is rendered for each of these columns. The name of the slot is the column's name. The slot exposes therow
&column
objects
<vue-table :path="route('system.menus.initTable')"
:error-handler="myErrorHandler"
:i18n="myI18n"
ref="table">
<template v-slot:my_column="{ row, column }">
<span>{{ row[column.name] }}
</template>
</vue-table>
- if you using custom totals in your table template, you may also add the template to be for the custom total
- if using preview option in you table template, a preview slot will also be available.
The name of the slot is
'preview'
EnsoTable
Is designed to be used within the Enso ecosystem, requiring minimal configuration from the dev.
Example:
<enso-table class="box is-paddingless raises-on-hover is-rounded"
:path="route('system.permissions.initTable')"
id="permissions"/>
Props
All the props from VueTable
can be provided here
Slots
The custom slots from VueTable
are available here
Filtering and Sorting
Sorting
In order for a column to be sortable, it needs to be marked in the
template as such ('sortable'
);
Once the columns are configured as searchable, when you want to sort using a certain column, it is enough to click the column's header.
Once a sorting has been applied, there will be a small 'x' button visible which you can use to cancel the sorting.
You may also use the reset button to clear all the table customizations including sorting and reload the template.
Filtering
In order to filter using a string of text, you may start typing in the search input.
All columns marked in the template as 'searchable'
(even those marked as 'notVisible'
or 'rogue'
) will be used for filtering. The search type is WHERE ... OR ...
.
For a brief example the following string abc will be used as search value. By default, there are five search modes available:
'startsWith'
- starting with the searched text (represented with ab*)'endsWith'
- ending with the searched text (represented with *ab)'exactMatch'
(represented with abc)'full'
- containing the searched text (represented with *a*)'doesntContain'
(represented withabc).- Note: Usually, when this search mode is applied on tables, only a single set of data
is targeted (ex: exclude rows if a specific and targeted data cell contains the search text).
When used on enso table all data sets (row's data cells) are targeted. Thus search will behave as follows: in the given example, rows will be excluded only if all data cells contain the string "abc" . - Note: Empty row data cells will be ignored by
'doesntContain'
search mode. So if there is a row that contains string "abc" and all the other fields are empty, then it will be considered that all row data cells (in this case just one) contain the string and thus the row will be excluded.
- Note: Usually, when this search mode is applied on tables, only a single set of data
is targeted (ex: exclude rows if a specific and targeted data cell contains the search text).
Also, the default search mode is to look for attributes starting with the searched text
as it's faster than 'full'
and more useful than 'exactMatch'
.
In the interface, if you want to switch between the search modes, you can use the button that appears to the right side of the search input, once you've typed something.
The button can show a maximum of three characters. To indicate the mode you're in, it will
display either 3 letters of whatever you've typed or 1-2 letters, replacing the remaining space
with asterisk wildcard (*
).
Ex: if the search word is "abc", the button will be illustrated like: "ab*", "*ab", "abc"
"*a*", "abc".
The search filter is case-insensitive.
The available modes as well as the default mode can be customized in the
tables
config.
Once the mode has been changed in the interface, the preferences are saved in the browser's local storage.
If you want to remove the search filtering, clear the search input.
You may also use the reset button to clear all the table customizations including searching and reload the template.
Advanced Filtering
Beyond the automatic filtering available out of the box, the VueJS component takes a set parameters which you can use on the back-end to customize the query:
- intervals
- filters
- params
Intervals are meant for applying date/dateTime restrictions on your results, are applied automatically and should respect table and column names:
intervals: {
sales: {
date: {
min: null,
max: null,
dateFormat: null,
},
},
Please take a look at the CoreTable parameters section above for more information
on the value of dateFormat
.
Filters are meant for applying generic restrictions on your results, are applied automatically and should also respect table and column names:
filters() {
return {
products: {
id: null,
},
};
},
Params are meant for building custom logic restrictions on your results and are NOT applied automatically. As such the parameter structure and attribute names depend on your choices.
params: {
fulfilled: null,
client: null,
The parameters object will be made available in your table builder specific methods. Please be sure to read the advanced usage section of the documentation for more information.
Table Init Sample Response
Below is the response from the back-end when the table is initialized. The sample is taken from the https://www.laravel-enso.com/system/menus/ index page.
{
"template":{
"crtNo":true,
"buttons":{
"global":[
{
"icon":"file-excel",
"class":null,
"event":"export-excel",
"action":"export",
"label":"Excel",
"name":"excel",
"path":"\/api\/system\/menus\/exportExcel"
},
{
"icon":"plus",
"class":null,
"event":"create",
"action":"router",
"label":"Create",
"name":"create",
"route":"system.menus.create"
}
],
"row":[
{
"icon":"pencil-alt",
"class":"is-row-button",
"event":"edit",
"action":"router",
"name":"edit",
"route":"system.menus.edit"
},
{
"icon":"trash-alt",
"class":"is-row-button",
"event":"destroy",
"action":"ajax",
"method":"DELETE",
"message":"The selected record is about to be deleted. Are you sure?",
"confirmation":true,
"postEvent":"destroyed",
"name":"destroy",
"path":"\/api\/system\/menus\/dtRowId"
}
]
},
"appends":[
"computedIcon"
],
"columns":[
{
"label":"Name",
"name":"name",
"data":"menus.name",
"meta":{
"average":false,
"boolean":false,
"clickable":false,
"cents":false,
"customTotal":false,
"date":false,
"datetime":false,
"filterable":false,
"icon":false,
"notExportable":false,
"nullLast":false,
"searchable":true,
"rawTotal":false,
"rogue":false,
"slot":false,
"sortable":true,
"translatable":false,
"total":false,
"notVisible":false,
"visible":true,
"hidden":false,
"sort":null
}
},
{
"label":"Icon",
"name":"computedIcon",
"data":"menus.computedIcon",
"meta":{
"average":false,
"boolean":false,
"clickable":false,
"cents":false,
"customTotal":false,
"date":false,
"datetime":false,
"filterable":false,
"icon":true,
"notExportable":true,
"nullLast":false,
"searchable":false,
"rawTotal":false,
"rogue":false,
"slot":false,
"sortable":false,
"translatable":false,
"total":false,
"notVisible":false,
"visible":true,
"hidden":false,
"sort":null
}
},
{
"label":"Parent",
"name":"parent",
"data":"parent_menus.name",
"meta":{
"average":false,
"boolean":false,
"clickable":false,
"cents":false,
"customTotal":false,
"date":false,
"datetime":false,
"filterable":false,
"icon":false,
"notExportable":false,
"nullLast":false,
"searchable":true,
"rawTotal":false,
"rogue":false,
"slot":false,
"sortable":true,
"translatable":false,
"total":false,
"notVisible":false,
"visible":true,
"hidden":false,
"sort":null
}
},
{
"label":"Route",
"name":"route",
"data":"permissions.name",
"meta":{
"average":false,
"boolean":false,
"clickable":false,
"cents":false,
"customTotal":false,
"date":false,
"datetime":false,
"filterable":false,
"icon":false,
"notExportable":false,
"nullLast":false,
"searchable":true,
"rawTotal":false,
"rogue":false,
"slot":false,
"sortable":true,
"translatable":false,
"total":false,
"notVisible":false,
"visible":true,
"hidden":false,
"sort":null
}
},
{
"label":"Has Children",
"name":"has_children",
"data":"menus.has_children",
"meta":{
"average":false,
"boolean":true,
"clickable":false,
"cents":false,
"customTotal":false,
"date":false,
"datetime":false,
"filterable":false,
"icon":false,
"notExportable":false,
"nullLast":false,
"searchable":false,
"rawTotal":false,
"rogue":false,
"slot":false,
"sortable":false,
"translatable":false,
"total":false,
"notVisible":false,
"visible":true,
"hidden":false,
"sort":null
}
},
{
"label":"Order",
"name":"order_index",
"data":"menus.order_index",
"meta":{
"average":false,
"boolean":false,
"clickable":false,
"cents":false,
"customTotal":false,
"date":false,
"datetime":false,
"filterable":false,
"icon":false,
"notExportable":false,
"nullLast":false,
"searchable":false,
"rawTotal":false,
"rogue":false,
"slot":false,
"sortable":true,
"translatable":false,
"total":false,
"notVisible":false,
"visible":true,
"hidden":false,
"sort":null
}
},
{
"label":"Created At",
"name":"created_at",
"data":"menus.created_at",
"meta":{
"average":false,
"boolean":false,
"clickable":false,
"cents":false,
"customTotal":false,
"date":true,
"datetime":false,
"filterable":false,
"icon":false,
"notExportable":false,
"nullLast":false,
"searchable":false,
"rawTotal":false,
"rogue":false,
"slot":false,
"sortable":true,
"translatable":false,
"total":false,
"notVisible":false,
"visible":true,
"hidden":false,
"sort":null
}
}
],
"model":"menu",
"table":"menus",
"dtRowId":"id",
"lengthMenu":[
10,
15,
20,
25,
30
],
"debounce":350,
"method":"GET",
"labels":{
"crtNo":"#",
"actions":"Actions"
},
"comparisonOperator":"LIKE",
"responsive":true,
"searchModes":[
"full",
"startsWith",
"endsWith",
"exactMatch",
"doesntContain"
],
"dateFormat":"d-m-Y",
"selectable":false,
"preview":false,
"name":"menus",
"readPath":"\/api\/system\/menus\/tableData",
"defaultSort":"menus.id",
"align":[
"has-text-centered"
],
"style":[
"is-hoverable",
"is-striped"
],
"aligns":{
"center":"has-text-centered",
"left":"has-text-left",
"right":"has-text-right"
},
"styles":{
"bordered":"is-bordered",
"compact":"is-narrow",
"hover":"is-hoverable",
"striped":"is-striped"
},
"highlight":"has-background-info",
"controls":[
"length",
"columns",
"style",
"reload",
"reset"
],
"actions":true
},
"meta":{
"start":0,
"search":"",
"loading":false,
"forceInfo":false,
"filterable":false,
"searchable":true,
"sort":false,
"total":false,
"date":true,
"datetime":false,
"translatable":false,
"enum":false,
"cents":false,
"money":false,
"resource":false,
"length":10,
"searchMode":"full",
"fullInfoRecordLimit":1000000
},
"apiVersion":"3.4"
}
Table Data Sample Response
Below is the response given by the backend when searching for something via the search input.
{
"data":[
{
"id":1,
"name":"core.home.index",
"description":"App State Builder",
"type":"Read",
"created_at":"08-09-2019",
"is_default":true,
"isRead":true
}
],
"count":444,
"filtered":1,
"total":[
],
"fullRecordInfo":true,
"filters":true
}
Questions & Issues
For questions and support please use the issues functionality for this package's github repository.
Please make sure to search for existing issues before creating a new issue, and when opening a new issue, fill the required information in the issue template.
Issues not conforming to the guidelines may be closed immediately.
Contributions
are welcome. Pull requests are great, but issues are good too.
Thank you to all the people who already contributed to Enso!
License
← Vue Switch Tabs →