Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Vue data table plugin

License

NotificationsYou must be signed in to change notification settings

uwla/vue-data-table

Repository files navigation

VueDataTable is a Vue plugin to easily create fully-featured data tables.

Check out my other plugin,vue-form-builder,that automatically generates beautiful forms from declarative rules.

FEATURES

  • Pagination
  • Search filter
  • Single column sorting
  • Multiple column sorting
  • Customize every visible text
  • Support for multiple languages
  • Export data (JSON, CVS, TXT or XLS)
  • Action buttons (view, edit, delete)
  • Editable cells (edit cell values)
  • Custom Vue components to render cells
  • Custom Footer to display data summary
  • Support for Vue3 and Vue2
  • Nuxt integration
  • Laravel integration

DEMO

The best way to see if a package suits your needs is by viewing and testing itsfunctionalities via ademo app.

There is also thisCodeSandbox Playgroundin which you can edit the source code with live preview.

Vue Data Table Demo 1Vue Data Table Demo 2Vue Data Table Demo 3

GETTING STARTED

Installation

npm install @uwlajs/vue-data-table

Make sure to install version2.0.0 or above for Vue3.

Versions prior to2.0.0 are for Vue2. Checkout thevue2 branch for its documentation.

Set up

importVueDataTablefrom"@uwlajs/vue-data-table";Vue.component("vue-data-table",VueDataTable);

Don"t forget to add the style sheets

import"@uwlajs/vue-data-table/dist/VueDataTable.css";

Usage

<template>    <div>        <data-tablev-bind="bindings"/>    </div></template><script>exportdefault {    computed: {bindings() {return {                columns: [/*the columns*/]                data: [/*the data*/]/* other props...*/            }        }    },}</script>

Note Notice that v-bind will take all key-value pairs in the object (in thiscase, thebindings), and pass them as props to theVueDataTable. So, this isa shortcut to pass multiple props at once.

Nuxt integration

Create a file@/plugins/vue-data-table.js, or whatever name you wish, with the following content:

importVueDataTablefrom'@uwlajs/vue-data-table'import'@uwlajs/vue-data-table/dist/style.css'exportdefaultdefineNuxtPlugin(nuxtApp=>{nuxtApp.vueApp.use(VueDataTable)})

Nuxt automatically loads the files in theplugins/ directory by default.

Laravel integration

This plugin integrates with Laravel's pagination API, so it fetches dataasynchronously from the provided URL. Follow the instrunctions in theasync data section for a detailed setup.

CONFIGURATION

Onlycolumns are required. Other props are optional.

Ifdata is not passed, thenfetchUrl andfetchCallbackmust be passed.

vKey is not required but ishighly recommend to set it if you plan toadd or delete rows in the table!

proptypedefaultdescription
allowedExportsArray["csv", "json", "txt"]Formats the user can export the data to. Allowed values:csv,json,txt,xlsx
dataArray-Array of objects with the data to be displayed on the table
columnsArray-Array of objects to specify how to render each column. Optional ifcolumnKeys is set
columnKeysArray-Array of strings matching the object keys indata. Discarded ifcolumns is set
langStringenThe default language
perPageSizesArray[10, 25, 50, 100, '*']The options for the number of rows being displayed per page. The string '*' shows all.
defaultPerPageNumber10The default number of entries. If unset, then it will be the first value ofperPageSizes
fetchUrlString-The URL to fetch data from ifdata is null
fetchCallbackString-Async function which takes an URL and returnsdata matching Laravel's pagination API
isLoadingBoolfalseWhether table data is loading. Table rows are shown only if this value is set tofalse
loadingComponentString,Object-VueJS component to be shown ifisLoading is set totrue
showPerPageBooltrueWhether to show thePerPage component
showEntriesInfoBooltrueWhether to show theEntriesInfo component
showSearchFilterBooltrueWhether to show theSearchFilter component
showPaginationBooltrueWhether to show thePagination component
showDownloadButtonBooltrueWhether to show the button to download the table's data
tableClassStringtable table-striped table-hoverThe table's HTMLclass attribute
sortingModeStringmultipleWhether to sort a single column or multiple columns at once
sortingIndexComponentString,ObjectVdtSortingIndexVueJS component for the sorting index on sortable columns
sortingIconComponentString,ObjectVdtSortingIconVueJS component for the sorting icon on sortable columns
footerComponentString,ObjectnullVueJS component for custom table footer
vKeyString-Thev-key, the key indata used by Vue to track and distinguish array elements.

Columns

keytypedefaultdescription
keyString-The object field to be displayed in a table cell
titleStringtitleCase(key)The title displayed in the header
searchableBooltrueWhether to allow searching rows by this column field
sortableBooltrueWhether to allow sorting the data by this column field
editableBooltrueWhether the column is editable by the user
collapsibleBoolfalseWhether the column is collapsible (expand and collapse)
typeStringstringData type ofkey. Allowed values:string,number
compareFunctionFunction-Custom function provided by the user to sort the column
searchFunctionFunction-Custom function provided by the user to search the column
indexNumber1000Lower values shift the column to the left of the table
componentString,Object-Custom Vue component to render inside table cell
componentPropsObject-Props to pass to the custom component

Ifcolumns is not defined, thencolumnKeys must be defined and it will bemapped to acolumns array with the default parameters. Example:

// we can define the columnsconfig={data:users,columns:[{key:"name",},{key:"email",title:"Email Address",sortable:false,},{key:"phone",sortable:false,searchable:false,index:1,// smaller indexes means the column is shift to the left},{key:"permissions",/* custom function sort users by which user has more permissions */compareFunction:function(a,b){// permissions is an arrayreturna.permissions.length-b.permissions.length;},/* custom function to allow searching the permission array */searchFunction:function(search,data){returndata.permissions.some(permission=>permission.includes(search))},searchable:true,/* custom component to display the permissions */component:UserPermissionList,}]}// or use columnKeys shortcutconfig={data:user,columnKeys:["name","email","registered_at","last_access_at"]},// which will take the default column and map the array into this[{key:"name",title:"Name",sortable:true,searchable:true,index:1000},{key:"email",title:"Email",sortable:true,searchable:true,index:1000},{key:"registered_at",title:"Registered At",sortable:true,searchable:true,index:1000},{key:"last_access_at",title:"Last Access At",sortable:true,searchable:true,index:1000},]

Custom Cell Component

Custom cell components must have adata property to receive the data of the currentrow for the component to display it.

In the previous code snippet, we used our custom componentUserPermissionList.Below is a sample of that custom component.

<template>    <div>        List of permissions for the user {{ data.name }} :        <ul>            <liv-for="(permission, i) in data.permissions":key="i">                {{ permission }}            </li>        </ul>    </div></template><script>exportdefault {    name:"UserPermissionList",    props: {        data: {            type:Object,            required:true        }    }}</script>

To handle events triggered by a custom component (such as clicking a button in acomponent), the component should emit an event calleduserEvent and pass anarbitrary payload to it. The event will be propagated upwards byVueDataTable,which will also emit an event calleduserEvent whose payload is the same asthe one emitted by the custom component. For example:

<template>    <inputtype="checkbox"class="form-control":checked="value"@change='toggleChecked' /></template><script>export {name: 'CheckboxCell',data() {return {value:false,        }    },    methods: {toggleChecked() {constpayload= {                id:this.data.id,                checked:this.value,            }this.$emit('userEvent', payload)        }    },    props: {        data:Object,    }}</script>

When the users clicks the checkbox, it will emit anuserEvent event, which canbe accessed from theVueDataTable. Here is an continuation of the previousexample.

<template>    <divclass="dashboard">        <h1>DASHBOARD</h1>        <buttonclass="btn btn-danger">            DELETE SELECTED ROWS        </button>        <vue-data-table:data="data":columns="columns"@userEvent="handleEvent" />    </div></template><script>exportdefault {data() {return {            data: [/**/],            columns: [/**/],            selectedRows: [],        }    },    methods: {handleEvent(payload) {const {checked,id }= payloadif (checked===true) {if (!this.selectedRows.includes(id))this.selectedRows.push(id)            }else {this.selectedRows=this.selectedRows.filter(rowId=> rowId!== id)            }        },deleteRows() {this.data=this.data.filter(row=>!this.selectedRows.includes(row.id))this.selectedRows= []        }    }}</script>

In the code snippet above, when the user checks the checkbox rendered by thecustom componentCheckboxCell, it will emit an event that is handled by themethodhandleEvent. This method will add/remove theid of the row to/fromtheselectedRows array. When the user clicks the "dangerous delete button", itwill deleted the selected rows from the table (on the client side only).

Action Buttons

VueDataTable provides a component calledVdtActionButtons, which can be usedto display buttons for common CRUD action such as viewing, editing, deleting.

Here is an example with all buttons (view, edit, delete) in one column:

<template>    <main>        <h1>DASHBOARD</h1>        <vue-data-tablev-bind="params"@userEvent="handleUserEvent"/>    </main></template><script>import {VdtActionButtons }from'@uwlajs/vue-data-table'exportdefault {data() {return {            params: {                data: users,                columns: [                    { key:'name' },                    { key:'job' },                    { component: VdtActionButtons, title:"actions" },                ],            },        }    },    methods: {handleUserEvent(payload) {console.log(payload.action,payload.data.name)        }    }}</script>

Another example, this time one button per column:

<template>    <main>        <h1>DASHBOARD</h1>        <vue-data-tablev-bind="params"@userEvent="handleUserEvent"/>    </main></template><script>import {VdtActionButtons }from'@uwlajs/vue-data-table'exportdefault {data() {return {            params: {                data: users,                columns: [                    { key:'name' },                    { key:'job' },                    {                        title:"view"                        component: VdtActionButtons,                        componentProps: { actions: ["view"] },                    },                    {                        title:"edit"                        component: VdtActionButtons,                        componentProps: { actions: ["edit"] },                    },                    {                        title:"delete"                        component: VdtActionButtons,                        componentProps: { actions: ["delete"] },                    },                ],            },        }    },    methods: {handleUserEvent(payload) {console.log(payload.action,payload.data.name)        }    }}</script>

When an user click an action button,VueDataTable will emit an event whosepayload is an object with two fields:action anddata. Theaction is thename of the action (view, edit, delete) anddata is the data of the row.

Check out the demo to see a real working example of using action buttons.

Editable cells

It is possible to make a column editable by settingseditable to true in thecolumn definition.

columns:{['key':name,editable:true],['key':email,editable:true],['key':phone,editable:true],// ...}

This will makeVueDataTable display anedit button on the right side of thecell's text. When the user clicks the button, it will show an input, so the usercan enter a new value for the cell. The user can cancel the editing or confirm.If the user confirms editing,VueDataTable will emit auserEvent whosepayload looks like the following:

{action:'updateCell',key:'<key String>',data:'<data Object>',value:'<value String>',}

Wherekey is the key of the column (if user edits thename column, thekeywill bename), thedata is the object of the row which was edit (an example:{ id: 100, name: 'joe', email: 'joe@email.test' }), andvalue is the valueinserted by the user (such asJoe Doe).

It is up to the developer to handle the event to update the row by, for example,sending an AJAX request to the API, then updating thedata array on the clientside. Here is an example of how to update the data array on the client side:

<template>  <vue-data-table:data="data":columns="columns"@userEvent="handleUserEvent"/></template><script>exportdefault {/* ...*/    methods: {handleUserEvent(payload) {if (payload.action==='updateCell')            {// send some ajax request// ...// then update the cellthis.updateDataCell(payload.data,payload.key,payload.value)            }else {// some other event            }        },updateDataCell(row,field,value) {let ind=this.data.findIndex(r=>r.id===row.id)if (ind<0)returnlet newRow= {...this.data[ind]}            newRow[field]= valuethis.data.splice(ind,1, newRow)        },    }}</script>

Selectable rows

VueDataTable provides the built-invdt-cell-selectable component to selecttable rows.

constprops={    columns=[{title:"",component:"vdt-cell-selectable"// <-- ADD THIS},{key:"name"},{key:"email"},/* ... */],    vKey="id",};constdata=[{id:1,name:"joe",email:"joe@example.com"},/* ... */]

When the user toggles the checkbox,VueDataTable emits an event calleduserEvent with the following payload:

{action:"select",selected:true||false,// this is the current value of the checkboxdata:{},// this is the current row (example, a user from an users array)}

You can have a reactive variable to keep track of selected items:

constselected=ref([]);consthandleSelect(payload){constitem=payload.data;if(payload.selected){selected.value.push(item);}else{selected.value=selected.value.filter((x)=>x.id!==item.id);}}

You can use this variable to perform bulk operations, such as mass deletion ormass edition.

Text

Currently,VueDataTable has support for the following languages: English (en),Brazilian Portuguese (pt-br), and Spanish(es). Thelang prop specifies inwhich language to display the text in our table.

If we want to add a custom text (maybe because there is no language support orbecause we want something else), we have to set it in thetext prop.

The following table shows the texts we can customize and their default valuesfor the English language.

keydefault
perPageText"Show :entries entries"
perPageAllText"ALL"
infoText"Showing :first to :last of :total entries"
infoAllText"Showing all entries"
infoFilteredText"Showing :first to :last of :filtered (filtered from :total entries)"
nextButtonText"Next"
previousButtonText"Previous"
paginationSearchText"Go to page"
paginationSearchButtonText"GO"
searchText"search:"
downloadText"export as:"
downloadButtonText"DOWNLOAD"
emptyTableText"No matching records found"

Note: Notice that the placeholders:first,:last,:total, and:filtered will be automatically replaced with the proper numbers.

Example code:

parameters(){return{data:[/**/],columns:[/**/],text:{PerPageText:"Number of users per page :entries",infoText:"Displaying :first to :last of :total users",emptyTableText:"No users found :(",}}}

Adding Language

If your language is not yet supported, you can add a new language and use it inanyVueDataTable instance as follow:

import{languageServiceProvider}from"@uwlajs/vue-data-table";constloremIpsumLanguage={perPageText:"lorem ipsum",nextButtonText:"labore ipsum",/* more ... */};languageServiceProvider.setLang("lorem",loremIpsumLanguage)

You can also change any default text for an existing language and that willreflect the changes globally. For example:

// the default text for the download button in the export component is "export as"// we may want change that to "download as"languageServiceProvider.setLangText("en","downloadText","download as:")

Async data

If you do not want to fetch all data at once and pass it toVueDataTable viathedata prop, you can do so by defining:

  • fetchUrl: initial endpoint for the first ajax request to fetch data
  • fetchCallback: async function which takes an URL and returns a responsefollowing Laravel's pagination API.

Here is a samplefetchCallback:

<template>    <h1>Users</h1>    <vue-data-tablev-bind="vdtProps" /></template><script setup>constvdtProps= {    columns: [        { key:'name' },        { key:"email", title:"Email address" },    ],    fetchUrl:"http://app.test/api/users",    fetchCallback:async (url)=>fetch(url).then(response=>response.json())}</script>

The example above uses the browser's built-infetch, but you can also useaxios or Nuxt's$fetch under the hood. Just make sure the response returnedby the callback matches the following.

The response from thefetchCallback should look like this:

{"data": [    {"id":1,"name":"Miss Juliet Heidenreich","email":"alvera13@example.org"},    {"id":2,"name":"Heloise Boehm","email":"joany.feil@example.net"},    {"id":3,"name":"Antwon Collins","email":"xhills@example.com},    /* ... */  ],"current_page": 1,"first_page_url":"http://app.test/api/users?page=1","from":1,"last_page":23,"last_page_url":"http://app.test/api/users?page=23","links": [    {"url":null,"label":"&laquo; Previous","active":false },    {"url":"http://app.test/api/users?page=1","label":"1","active":true },    {"url":"http://app.test/api/users?page=2","label":"2","active":false },    {"url":"http://app.test/api/users?page=3","label":"3","active":false },/* ...*/    {"url":"http://app.test/api/users?page=23","label":"23","active":false },    {"url":"http://app.test/api/users?page=2","label":"Next &raquo;","active":false }  ],"next_page_url":"http://app.test/api/users?page=2","path":"http://app.test/api/users","per_page":15,"prev_page_url":null,"to":15,"total":340}

Here is how you do so in Laravel:

<?phpuseApp\Models\User;useIlluminate\Support\Facades\Route;Route::get('users',function () {return User::paginate();});

This will also work with collections:new UserCollection(User::paginate()).

In order to be able tosort andsearch using endpoints compatible withLaravel's API, this plugin provides support for Spatie's Laravel Query Builderpackage, which allows you to easily generate API endpoints with sorting andsearching functionalities with well-defined standard.

<?phpuseApp\Models\User;useIlluminate\Support\Facades\Route;useSpatie\QueryBuilder\QueryBuilder;Route::get('users',function () {return QueryBuilder::for (User::class)        ->allowedSorts(['name','email'])        ->allowedFilters(['name','email'])        ->paginate();});

The endpoints look like this:

  • http://app.test/api/users?page=1&filter[name]=foo
  • http://app.test/api/users?page31&sort=job,-email
  • http://app.test/api/users?page=1&sort=email&filter[email]=joe&filter=[name]=joe

You donot need to worry about the URLs if you are using Spatie's Laravel Query Bulder,becauseVueDataTable follows their endpoint standard and automatically generates the urls.

If you do not use their package, then you should parse theurl variable insidethefetchCallback, and modify the url. For example, your javascript code should modify:

http://app.test/api/users?page=4&filter[name]=foo --> http://app.test/api/users?page=4&search=foo.

Keep in mind that, by default, Spatie's Query Builder apply AND logic for allfilters. That means if you have&filter[name]=Ana&filter[email]=Ana, thenyou will only get results that bothname andemail fields match Ana. Ifname matches Ana but not theemail column, then this row would not appear.

Here is how you can implementOR logic using their package:

<?php// routes/app.phpuseApp\Http\Filters\FilterOrWhere;useApp\Models\User;useIlluminate\Support\Facades\Route;useSpatie\QueryBuilder\QueryBuilder;useSpatie\QueryBuilder\AllowedFilter;Route::get('users',function () {return QueryBuilder::for (User::class)        ->allowedSorts(['name','email'])        ->allowedFilters([            AllowedFilter::custom('name',newFilterOrWhere),            AllowedFilter::custom('email',newFilterOrWhere)        ])        ->paginate();});// app/Http/Filters/FilterOrWhere.phpnamespaceApp\Http\Filters;useSpatie\QueryBuilder\Filters\Filter;useIlluminate\Database\Eloquent\Builder;class FilterOrWhereimplements Filter{publicfunction__invoke(Builder$query,$value,string$property)    {$query->orWhere($property,'LIKE','%' .$value .'%');    }}

Layout

VueDataTable uses CSS's grid display to specify the position of its components(search filter, pagination, entries info, per page options, download button).

We can specify the position of the components by including our custom CSS/SCSSand overriding the defaults.

By default, this is howVueDataTable displays the components:

.data-table {display:grid;width:100%;grid-template-columns:25%25%25%25%;&>div {margin-top:1rem;max-width:100%;    }& >.data-table-search-filter,.data-table-pagination,.data-table-export-data {margin-left:auto    }@media (min-width:1401px) {grid-template-areas:"perPage search search search""table table table table""info pagination pagination download";    }@media (min-width:1051px) AND (max-width:1400px) {grid-template-areas:"perPage search search search""table table table table""info pagination pagination pagination"". . download download";    }@media (min-width:851px) AND (max-width:1050px) {grid-template-areas:"perPage search search search""table table table table""pagination pagination pagination pagination""info info download download";    }@media (max-width:800px) {& >.data-table-pagination {flex-wrap:wrap;        }    }@media (min-width:651px) AND (max-width:850px) {grid-template-areas:"perPage search search search""table table table table""pagination pagination pagination pagination""info info info info""download download download download";    }@media (max-width:650px) {grid-template-areas:"search search search search""perPage perPage perPage perPage""table table table table""pagination pagination pagination pagination""info info info info""download download download download";& >.data-table-per-page {margin-left:auto        }    }}

Feel free to copy the styles above, modify it, and then set the position of thecomponents as you want.

Custom Components

Besides a custom component for each column, you provide custom components forthe table's footer, the column'ssorting icon (the icon displayed if thecolumns is sorted), and the column'ssorting index (the index of the currentcolumn if it is being sorted and multi column sorting is enabled).

Footer

The propertyfooterComponent sets the component to render the table's footer.The component can be either the componentObject, or aString equals to thename of the registered component.

ThefooterComponent must be a<tfoot> HTML element and it must have thepropertiesdata,dataDisplayed,dataFiltered. If the component does notspecify those properties inprops, Vue will probably think they are somecustom HTML attribute and their values will be show as HTML attributes, which isreally messy.

The propertydata correspond to all data passed toVueDataTable. ThedataDisplayed corresponds to all data that is currently visible on the table.ThedataFiltered corresponds to all data that was filtered by a search query.These properties can be used to perform common operations such as calculatingthe sum of the values of the total rows of a certain column.

Suppose we have a table that of fruits. Thedata is an array of objects whoseproperties arename,price, andamount. We can provide a custom footer toshow the total amount of fruits bought and the total price.

The footer component would be something like:

<template>  <tfootv-show="dataDisplayed.length > 0">    <td>Total</td>    <td></td>    <td>{{ totalAmount }}</td>    <td>{{ totalPrice }}</td>  </tfoot></template><script>exportdefault {  name:"TableFooter",  computed: {totalPrice() {let s=0;for (let fofthis.dataDisplayed)        s+=f.price*f.amount;return s;    },totalAmount() {let s=0;for (let fofthis.dataDisplayed)        s+=f.amount;return s;    }  },  props: {    data:Array,    dataDisplayed:Array,    dataFiltered:Array,  }}</script>

And we pass this component as follow:

<template>    <data-tablev-bind="tableProps"/></template><script>importTableFooterfrom'./TableFooter.vue'exportdefault {/* ... some code*/data() {return {            tableProps: {                columns: [/* ... code*/ ],                data: [/* ... more code*/ ],                footerComponent: TableFooter,            }        }    }}</script>

Alternately, you can register the component and pass a string:

/* early on */importTableFooterfrom'./TableFooter.vue'Vue.component("table-footer",TableFooter)/* later on */    footerComponent:"table-footer"

Sort Icon

By default,VueDataTable will display arrows to indicate the sorting directionwhen sorting a column. TheSortingIcon component is wrapped in ath element.Theth element has adata-sorting attribute that may beasc ordesconly. Based on this value, we display anarrow_up or anarrow_down iconusingCSS rules.

<template>    <spanclass="data-table-sorting-icon">&nbsp;</span></template><style lang="scss" scoped>.data-table-sorting-icon {&::after {content:"\2193";    }&::before {content:"\2191";    }&::after,&::before {opacity:0.5;    }    [data-sorting="asc"]&::before, [data-sorting="desc"]&::after {opacity:1;    }}</style>

Note: Some code was omitted to keep it clean.

If we want to add our custom icons for this, then we can register our component,like so:

importSortingIconfrom"./path/to/SortIcon.vue";exportdefault{computed:{bindings(){return{SortingIconComponent:SortingIcon,data:[],/**/};}}}

Sorting Index Icon

When sorting multiple columns,VueDataTable will display an icon with a indexindicating which column has the priority in the sorting process.

<template>    <spanclass="data-table-sort-index">        {{ index }}    </span></template>

If we want to add our own component for this, we can register it just like wedid before.

importSortingIndexfrom"./path/to/SortingIndex.vue";exportdefault{computed:{bindings(){return{SortingIndexComponent:SortingIndex,data:[],/**/};}}};

In ourSortingIndex component, we must have aindex property, whichcorrespondent to the index of the column in the sorting process.

exportdefault{name:"SortingIndex",props:{index:{type:Number,required:true}}};

ROADMAP

  • Support for Vue3
  • Laravel integration
  • Support for SSR
  • String notation for defining columns

LICENSE

MIT

CONTRIBUTING

Pull requests are very welcome.

Packages

No packages published

Contributors2

  •  
  •  

[8]ページ先頭

©2009-2025 Movatter.jp