# Guide
# Getting Started
# Introduction
It's a data table created using Vue.js, which has some features like :
- Show data per page
- Sort columns
- Cells Custom rendering
- CRUD Actions
- Customize the columns display
- Filter data by fields
# Installation
npm install vueye-table --save
# Requirements
To make this component work with Vue 2, you should use the Vue composition-api plugin by importing it and using it in main.js
as follows :
import Vue from 'vue';
import App from './App.vue';
import VueComp from '@vue/composition-api';
Vue.use(VueComp);
new Vue({
render: h => h(App),
}).$mount('#app');
# Columns configuration
The columns
prop value could have the following structure :
columns: [
{
key: "id",
label: "ID",
sortable: true,
type: "number",
display: true
},
...
]
key | description |
---|---|
key | the corresponding field in the data array, it could be a path to a nested field |
label | the text to show in the table head |
sortable | Allow or not the column sorting |
type | the field type |
display | show the column |
# Examples
# Basic Example
Input
<template>
<VueyeTable
:header-display="false"
:data="employees"
:columns="columns"
title="Employees">
</VueyeTable>
</template>
<script>
import employees from "../assets/employees.js";
export default {
name: "BasicExample",
data: () => ({
// employees: employees,
columns: [
{
key: "id",
label: "ID",
sortable: true,
type: "number",
display: true
},
{
key: "employee_name",
label: "Employee Name",
sortable: true,
display: true
},
{
key: "employee_salary",
label: "Employee Salary",
display: true,
sortable: true
},
{
key: "employee_age",
label: "Employee Age",
sortable: true
},
{
key: "address.city",
label: "Address",
display: true,
sortable: true
}
]
}),
computed:{
employees(){
return employees.slice();
}
}
};
</script>
Output
# Display table header
The table header contains the title, search input and the export and print buttons
Input
<template>
<VueyeTable
:data="employees"
:columns="columns"
title="Employees">
</VueyeTable>
</template>
<script>
import employees from "../assets/employees.js";
export default {
name: "WithHeader",
data: () => ({
employees: employees,
columns: [
{
key: "id",
label: "ID",
sortable: true,
type: "number",
display: true
},
{
key: "employee_name",
label: "Employee Name",
sortable: true,
display: true
},
{
key: "employee_salary",
label: "Employee Salary",
display: true,
sortable: true
},
{
key: "employee_age",
label: "Employee Age",
sortable: true
},
{
key: "address.city",
label: "Address",
display: true,
sortable: true
}
]
})
};
</script>
Output
Employees
# CRUD
The actions
slot gives you an access to the row item that you could edit, delete or do any other action with it, the following example shows a simple use :
<template v-slot:actions="{item}">
<div class="ve-table-actions">
<button class="ve-table-btn ve-table-btn-primary" @click="edit(item)">Edit</button>
<button class="ve-table-btn ve-table-btn-danger" @click="deleteItem(item)">Delete</button>
</div>
</template>
Note that you're free to define your actions and the components which accomplish the full action like confirmation message and edit dialog
Input
<template>
<div id="app">
<VueyeTable :data="employees" :columns="columns" title="Employees" :config="config">
<template v-slot:header.cell.employee_salary="{columnDef}">
<th style="background:#e3e3e3;color:#000;display:flex;height:48px">
<span>{{columnDef.column.label}}</span>
<span @click="columnDef.sorter.handler(columnDef.column)">
<h4 v-if="columnDef.sorter.column.direction==='asc'">↑</h4>
<h4 v-else-if="columnDef.sorter.column.direction==='desc'">↓</h4>
<h4 v-else>↑</h4>
</span>
</th>
</template>
<template v-slot:cell.employee_salary="{item}">
<td style="background:#e3e3e3;color:#000;" data-label="employee_salary">
<b
v-if="item.employee_salary>100000"
style="background:#3bb640;color:white;padding:3px;border-radius:2px"
>{{item.employee_salary}}</b>
<b
v-else
style="background:#ee4422;color:white;padding:3px;border-radius:2px"
>{{item.employee_salary}}</b>
</td>
</template>
<template v-slot:header.cell.employee_name="{columnDef}">
<th class="ve-table-custom-cell">{{columnDef.column.label}}</th>
</template>
<template v-slot:cell.employee_name="{item}">
<td class="ve-table-custom-cell" data-label="Employee Salary">{{item.employee_name}}</td>
</template>
<template v-slot:actions="{item}">
<div class="ve-table-actions">
<button class="ve-table-btn ve-table-btn-primary" @click="edit(item)">Edit</button>
<button class="ve-table-btn ve-table-btn-danger" @click="deleteItem(item)">Delete</button>
</div>
</template>
</VueyeTable>
</div>
</template>
<script>
import employees from "../assets/employees.js";
export default {
name: "App",
data: () => ({
employees: employees,
columns: [
{
key: "id",
label: "ID",
sortable: true,
type: "number",
display: true
},
{
key: "employee_name",
label: "Employee Name",
sortable: true,
display: true
},
{
key: "employee_salary",
label: "Employee Salary",
display: true,
sortable: true
},
{
key: "employee_age",
label: "Employee Age",
sortable: true
},
{
key: "address.city",
label: "Address",
display: true,
sortable: true
},
{
key: "actions",
label: "Actions",
sortable: false,
display: true
}
],
selectedRows: [],
config: {
filterBy: "Chercher par",
search: "Chercher",
nbRowsPerPage: "Nombre des lignes par page",
of: "de"
}
}),
methods: {
edit(item) {
//open a dialog to edit the selected item
//or redirect to another page that contains
// edit form
},
deleteItem(item) {
this.employees = this.employees.filter(
employee => employee.id !== item.id
);
},
getMoreDetails(id) {
return this.employees.find(emp => emp.id === id);
}
},
components: {
}
};
</script>
<style lang="scss">
.ve-table {
&-actions {
width: 104px;
display: flex;
justify-content: space-around;
align-items: center;
}
&-btn {
height: 24px;
min-width: 32px;
padding: 0 8px;
text-align: center;
border-radius: 4px;
cursor: pointer;
justify-content: center;
outline: none;
border: none;
position: relative;
white-space: nowrap;
&-primary {
background: #3844cc;
color: white;
}
&-danger {
background: #e24e40;
color: white;
}
}
}
.ve-table-custom-cell {
background: rgb(30, 118, 233);
color: white;
}
</style>
Output
Employees
# Table body custom rendering
Input
<template>
<div id="app">
<VueyeTable :data="users" :columns="columns" title="Github users">
<template v-slot:avatar_url="{item}">
<img :src="item.avatar_url" alt class="ve-avatar">
</template>
<template v-slot:html_url="{item}">
<a target="_blank" :href="item.html_url">{{item.html_url}}</a>
</template>
</VueyeTable>
</div>
</template>
<script>
import users from "../assets/github_users.js";
export default {
name: "CustomRendering",
data: () => ({
users,
columns: [
{
key: "avatar_url",
label: "Avatar",
sortable: true,
display: true
},
{
key: "login",
label: "User name",
sortable: true,
display: true
},
{
key: "html_url",
label: "Profile",
display: true
}
],
})
};
</script>
<style lang="scss">
.ve-avatar {
height: 32px;
width: 32px;
border-radius: 50%;
border: thin solid #aaa;
}
</style>
Output
Github users
# Table body cells custom rendering
If you want to take the control over the whole cell you can prefix
the column
key with cell.
keyword and here you're able to style and render the td
element as you want
Note that you should add data-label
attribute with column name, this's useful for small screens
Input
<template>
<div id="app">
<VueyeTable :data="todos" :columns="columns" title="Todos">
<template v-slot:cell.completed="{item}">
<td :style="{'background':colors[item.userId-1],'color':'white'}" data-label="Completed">{{item.userId}}</td>
</template>
<template v-slot:completed="{item}">{{item.completed?'Yes':'No'}}</template>
</VueyeTable>
</div>
</template>
<script>
import todos from "../assets/todos.js";
export default {
name: "FullCellCustomRendering",
data: () => ({
todos,
columns: [
{
key: "id",
label: "ID",
sortable: true,
display: true
},
{
key: "userId",
label: "User ID",
sortable: true,
display: true
},
{
key: "title",
label: "Title",
display: true
},
{
key: "completed",
label: "Completed",
display: true
}
],
//we have 10 users and each one is defined by color, that color will be shown in the
//custom cells
colors: [
"#004D40",
"#00695C",
"#2e003e",
"#3d2352",
"#05386B",
"#379683",
"#022140",
"#265077",
"#0c0023",
"#fc0023",
]
})
};
</script>
<style lang="scss">
.ve-avatar {
height: 32px;
width: 32px;
border-radius: 50%;
border: thin solid #aaa;
}
</style>
Output
Todos
# Table head custom rendering
You could also customize the column
label in the table head.
Input
<template>
<div id="app">
<VueyeTable :data="users" :columns="columns" title="Github users">
<template v-slot:header.avatar_url="{column}">
{{column.label}} <i>(The user profile image)</i>
</template>
<template v-slot:header.html_url="{column}">
{{column.label}} <i>(Link to the user profile)</i>
</template>
<template v-slot:avatar_url="{item}">
<img :src="item.avatar_url" alt class="ve-avatar">
</template>
<template v-slot:html_url="{item}">
<a target="_blank" :href="item.html_url">{{item.html_url}}</a>
</template>
</VueyeTable>
</div>
</template>
<script>
import users from "../assets/github_users.js";
export default {
name: "HeadCustomRendering",
data: () => ({
users,
columns: [
{
key: "avatar_url",
label: "Avatar",
sortable: true,
display: true
},
{
key: "login",
label: "User name",
sortable: true,
display: true
},
{
key: "html_url",
label: "Profile",
display: true
}
],
})
};
</script>
<style lang="scss">
.ve-avatar {
height: 32px;
width: 32px;
border-radius: 50%;
border: thin solid #aaa;
}
</style>
Output
Github users
# Table head full cell custom rendering
You could be able to customize the full head cell of given column (not just the label), to make this you have access to a property called columnDef
which contains column
and the sorter
that has the following fields:
1 - handler
this function is used to sort the given column which should be passed as parameter
2 - column.direction
the sort direction, by default its value is none
if you click in the sort icon this passes to asc
, then to desc
, if you click again it comes back to none
and so on.
Input
<template>
<div id="app">
<VueyeTable :data="employees" :columns="columns" title="Employees">
<template v-slot:header.cell.employee_salary="{columnDef}">
<th style="background:#e3e3e3;color:#000;display:flex;">
<span>{{columnDef.column.label}}</span>
<span @click="columnDef.sorter.handler(columnDef.column)">
<h4 v-if="columnDef.sorter.column.direction==='asc'">↑</h4>
<h4 v-else-if="columnDef.sorter.column.direction==='desc'">↓</h4>
<h4 v-else>↑</h4>
</span>
</th>
</template>
<template v-slot:cell.employee_salary="{item}">
<td style="background:#e3e3e3;color:#000;">
<b
v-if="item.employee_salary>100000"
style="background:#3bb640;color:white;padding:3px;border-radius:2px"
>{{item.employee_salary}}</b>
<b
v-else
style="background:#ee4422;color:white;padding:3px;border-radius:2px"
>{{item.employee_salary}}</b>
</td>
</template>
<template v-slot:header.cell.employee_name="{columnDef}">
<th class="ve-table-custom-cell">{{columnDef.column.label}}</th>
</template>
<template v-slot:cell.employee_name="{item}">
<td class="ve-table-custom-cell">{{item.employee_name}}</td>
</template>
</VueyeTable>
</div>
</template>
<script>
import employees from "../assets/employees.js";
export default {
name: "HeadCellCustomRendering",
data: () => ({
employees: employees,
columns: [
{
key: "id",
label: "ID",
sortable: true,
type: "number",
display: true
},
{
key: "employee_name",
label: "Employee Name",
sortable: true,
display: true
},
{
key: "employee_salary",
label: "Employee Salary",
display: true,
sortable: true
},
{
key: "employee_age",
label: "Employee Age",
sortable: true
},
{
key: "address.city",
label: "Address",
display: true,
sortable: true
}
]
}),
methods: {}
};
</script>
<style lang="scss">
.ve-table-custom-cell {
background: rgb(30, 118, 233);
color: white;
}
</style>
Output
Employees
# Select rows
Input
<template>
<div>
<VueyeTable
:data="users"
:columns="columns"
select-rows
v-model="selectedUsers"
title="Users"
></VueyeTable>
<hr>
<pre class="language-js">
{{selectedUsers}}
</pre>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "BasicExample",
data: () => ({
users: [],
selectedUsers: [],
columns: [
{
key: "id",
label: "ID",
sortable: true,
type: "number",
display: true
},
{
key: "name",
label: "Name",
sortable: true,
display: true
},
{
key: "username",
label: "User name",
sortable: true,
display: true
},
{
key: "address.city",
label: "City",
display: true,
sortable: true
}
]
}),
created() {
axios
.get("https://jsonplaceholder.typicode.com/users")
.then(result => {
this.users = result.data;
})
.catch(err => {});
}
};
</script>
Output
Users
[]
# Expand rows
If there's more details and you don't want to overload your table, so you could put that details inside an expanded row with your custom style
Input
<template>
<div>
<VueyeTable :data="employees" :columns="columns" title="Employees">
<template v-slot:expand="{item}">
<div style="padding:10px">
<div>Details</div>
<div>
<ul>
<li v-for="(val, key,index) in getMoreDetails(item.id)" :key="index">
<strong>{{key}} :</strong>
{{val}}
</li>
</ul>
</div>
</div>
</template>
</VueyeTable>
</div>
</template>
<script>
import employees from "../assets/employees.js";
export default {
name: "App",
data: () => ({
employees: employees,
columns: [
{
key: "id",
label: "ID",
sortable: true,
type: "number",
display: true
},
{
key: "employee_name",
label: "Employee Name",
sortable: true,
display: true
},
{
key: "employee_salary",
label: "Employee Salary",
display: true,
sortable: true
},
{
key: "employee_age",
label: "Employee Age",
sortable: true
},
{
key: "address.city",
label: "Address",
display: true,
sortable: true
},
{
key: "actions",
label: "Actions",
sortable: false,
display: true
}
]
}),
methods: {
getMoreDetails(id) {
return this.employees.find(emp => emp.id === id);
}
}
};
</script>
Output
Employees
# Config default labels
I don't like to use i18n
configuration in order to make the component internationalized, but i prefered to give the developer the possibility to make its own config.
The default config :
{
filterBy: "Filter by",
search: "Search",
nbRowsPerPage: "Number of rows per page",
of: "of"
}
Input
<template>
<VueyeTable
:data="employees"
:columns="columns"
title="Employees"
:config='config'
>
</VueyeTable>
</template>
<script>
import employees from "../assets/employees.js";
export default {
name: "ConfigDefaultLabels",
data: () => ({
employees: employees,
config: {
filterBy: "Chercher par",
search: "Chercher",
nbRowsPerPage: "Nombre des lignes par page",
of: "de"
},
columns: [
{
key: "id",
label: "ID",
sortable: true,
type: "number",
display: true
},
{
key: "employee_name",
label: "Employee Name",
sortable: true,
display: true
},
{
key: "employee_salary",
label: "Employee Salary",
display: true,
sortable: true
},
{
key: "employee_age",
label: "Employee Age",
sortable: true
},
{
key: "address.city",
label: "Address",
display: true,
sortable: true
}
]
})
};
</script>
Output
Employees
# Server side pagination
if your data needs a server request with per page or page number queries so you could do that by providing a server request prop as follows :
:server="serverRequest"
and in your data object :
serverRequest: {
total: null,//this will be updated by server response
perPage: 10,
page: 1
}
in order to go to the next/previous page or change per page value, the table component should emit an event called @update-request
and its handler has two parameters page
and perPage
, then the method will update the server request and fires an AJAX call to fetch the data based on the previous parameters
TIP
you don't need to call the method that fetch data in the mounted
or created
hooks because is called when the page
changes which means that the method is called for the first rendering
Input
<template>
<div id="app">
<VueyeTable
:data="answers"
:columns="columns"
title="answers"
:server="serverRequest"
@update-request="updateRequest"
>
<template v-slot:title="{item}">
<a :href="item.link" target="_blank" rel="noopener noreferrer">
{{item.title}}
</a>
</template>
<template v-slot:score="{item}">
<h3 :style="{color:item.is_accepted?'#4d4':'#777'}" v-if="item.score">
+
<span>{{item.is_accepted?(item.score*10)+15:(item.score*10)}}</span>
</h3>
</template>
<template v-slot:creation_date="{item}">
<span>{{new Date(Number(item.creation_date+'000')).toDateString()}}</span>
</template>
</VueyeTable>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "App",
data: () => ({
answers: [],
columns: [
{
key: "answer_id",
label: "ID",
sortable: true,
type: "number",
display: true
},
{
key: "title",
label: "Title",
sortable: true,
display: true
},
{
key: "score",
label: "Score",
display: true,
sortable: true
},
{
key: "creation_date",
label: "Date",
sortable: true,
display: true
},
{
key: "is_accepted",
label: "Accepted",
sortable: true,
},
{
key: "link",
label: "Link",
sortable: true,
}
],
serverRequest: {
total: null,
perPage: 10,
page: 1
}
}),
methods: {
updateRequest(page, perPage) {
this.serverRequest = { ...this.serverRequest, page, perPage };
this.getData();
},
getData() {
axios
.get(
`https://api.stackexchange.com/2.2/users/8172857/answers?page=${
this.serverRequest.page
}&pagesize=${
this.serverRequest.perPage
}&order=desc&sort=activity&site=stackoverflow&filter=!)sAFO_2))PMwRocnks3p`
)
.then(result => {
this.answers = result.data.items;
this.serverRequest.total = result.data.total;
this.serverRequest.perPage = result.data.page_size;
this.serverRequest.page = result.data.page;
})
.catch(err => {});
}
},
created() {
// this.getData();
}
};
</script>
<style lang="scss">
#app {
display: flex;
justify-content: center;
align-items: center;
padding: 24px;
box-shadow: 0 0 2px #aaa;
}
</style>
Output
answers
# Props
Name | Description |
---|---|
title | the data table title |
columns | the attributes or columns |
data | JS array of objects or json content |
filter-by | specify the default column for filter |
per-page-values | the array of per pages values |
per-page | the default per page |
select-rows | add checkbox columns in order to select rows |
v-model | returns the selected rows |
dense | Show table rows in small size |
headerDisplay | show/hide the table header |
config | change the default labels |
server | the server request with per page, page number and total queries |
# Events
Name | Description |
---|---|
update-request | if you provide server prop you'll need this event to get the some parameter from the table like page and perPage |