# 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'">&#8593;</h4>
            <h4 v-else-if="columnDef.sorter.column.direction==='desc'">&#8595;</h4>
            <h4 v-else>&#8593;</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'">&#8593;</h4>
            <h4 v-else-if="columnDef.sorter.column.direction==='desc'">&#8595;</h4>
            <h4 v-else>&#8593;</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