README
BootstrapVue Editable Table
BootstrapVue Editable Table is a Vue table component that enables cell editing and inherits/supports all other features from BootstrapVue Table.
This is still an early stage beta version so please help by creating issues with proper labels (bug, question, enhancement...). Thank you in advance 🙏
If you'd like to contribute, please read this introductory article to understand the basic code structure. Whenever you are ready, just create a pull request 👍
Table of Contents
Prerequisite
Setup
Usage
Data Binding
Form Elements
Column Width
Custom Styling
Keyboard Keys
Events
Edit Properties
Custom Cell
Add and Remove Rows
Load Data via REST API
Supported Version
Roadmap
Prerequisite
A basic understanding of BootstrapVue Table.
You're required to install Bootstrap and Bootstrap Vue in your project:
npm install bootstrap bootstrap-vue
Setup
npm i bootstrap-vue-editable-table
Since this is a BootstrapVue component, you need to set it up the same way. The easiest approach is to register BootstrapVue in your app entry point (typically app.js or main.js):
import Vue from 'vue'
import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'
// Import Bootstrap and BootstrapVue CSS files (order is important)
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
// Make BootstrapVue available throughout your project
Vue.use(BootstrapVue)
// Optionally install the BootstrapVue icon components plugin
Vue.use(IconsPlugin)
Please refer to BoostrapVue Docs for more details.
Usage
<template>
<div>
<b-editable-table bordered class="editable-table" v-model="items" :fields="fields" @input-change="handleInput">
<template #cell(isActive)="data">
<span v-if="data.value">Yes</span>
<span v-else>No</span>
</template>
</b-editable-table>
</div>
</template>
<script>
import BEditableTable from 'bootstrap-vue-editable-table';
export default {
components: {
BEditableTable
},
data() {
return {
fields: [
{ key: "name", label: "Name", type: "text", editable: true, placeholder: "Enter Name...", class: "name-col"},
{ key: "department", label: "Department", type: "select", editable: true, class: "department-col" , options: [
{ value: 1, text: 'HR' },
{ value: 2, text: 'Engineer' },
{ value: 3, text: 'VP' },
{ value: 4, text: 'CEO'}
]},
{ key: "age", label: "Age", type:"range", min:"0", max:"100", editable: true, placeholder: "Enter Age...", class: "age-col" },
{ key: "dateOfBirth", label: "Date Of Birth", type: "date", editable: true, class: "date-col", locale: "en",
"date-format-options": {
year: "numeric",
month: "numeric",
day: "numeric",
}, },
{ key: "isActive", label: "Is Active", type: "checkbox", editable: true, class: "is-active-col" }
],
items: [
{ age: 40, name: 'Dickerson', department: 1, dateOfBirth: '1984-05-20', isActive: true },
{ age: 21, name: 'Larsen', department: 2, dateOfBirth: '1972-07-25', isActive: false },
{ age: 89, name: 'Geneva', department: 3, dateOfBirth: '1981-02-02', isActive: false },
{ age: 38, name: 'Jami', department: 4, dateOfBirth: '1964-10-19', isActive: true },
]
};
},
methods: {
handleInput(value, data) {}
}
};
</script>
<style>
table.editable-table {
margin: auto;
}
table.editable-table td {
vertical-align: middle;
}
.editable-table .data-cell {
padding: 8px;
vertical-align: middle;
}
.editable-table .custom-checkbox {
width: 50px;
}
.name-col {
width: 120px;
}
.department-col {
width: 150px;
}
.age-col {
width: 100px;
}
.date-col {
width: 200px;
}
.is-active-col {
width: 100px
}
</style>
items
and fields
are the same properties used in BootstrapVue Table except we are introducing a new type
and editable
property in the fields
object to indicate what element is required in every column and whether or not it should be editable. Also, v-model
is supported for two-way binding but you can still use :items
instead for one-way binding. More on that in the Data Binding section
For select
element, options can be passed as another property (as shown in the example above). Since this is a Boostrap Form Select, it supports a list of strings or key/value objects:
[
{ value: 'a', text: 'First option' },
{ value: 'b', text: 'Second Option' },
{ value: 'b', text: 'Third Option' }
]
Data Binding:
|Data | Binding | |--|--| | :items="items" | One-way binding | v-model="items" | Two-way binding
When using v-model
the data will be updated automatically:
<b-editable-table v-model="items" :fields="fields"></b-editable-table>
Otherwise, using :items
prop to pass data will require updating the data manually on input change:
<template>
<div>
<b-editable-table :items="items" :fields="fields" @input-change="handleInput">
<template #cell(isActive)="data">
<span v-if="data.value">Yes</span>
<span v-else>No</span>
</template>
</b-editable-table>
</div>
</template>
<script>
import BEditableTable from 'bootstrap-vue-editable-table';
export default {
components: {
BEditableTable
},
data() {
return {
fields: [
{ key: "name", label: "Name", type: "text", editable: true, placeholder: "Enter Name..."},
{ key: "department", label: "Department", type: "select", editable: true, class: "department-col" , options: [
{ value: 1, text: 'HR' },
{ value: 2, text: 'Engineer' },
{ value: 3, text: 'VP' },
{ value: 4, text: 'CEO'}
]},
{ key: "age", label: "Age", type:"range", min:"0", max:"100", editable: true, placeholder: "Enter Age..." },
{ key: "dateOfBirth", label: "Date Of Birth", type: "date", editable: true, class: "date-col", locale: "en",
"date-format-options": {
year: "numeric",
month: "numeric",
day: "numeric",
}, },
{ key: "isActive", label: "Is Active", type: "checkbox", editable: true },
],
items: [
{ age: 40, name: 'Dickerson', department: 1, dateOfBirth: '1984-05-20', isActive: true },
{ age: 21, name: 'Larsen', department: 2, dateOfBirth: '1972-07-25', isActive: false },
{ age: 89, name: 'Geneva', department: 3, dateOfBirth: '1981-02-02', isActive: false },
{ age: 38, name: 'Jami', department: 4, dateOfBirth: '1964-10-19', isActive: true }
]
};
},
methods: {
handleInput(value, data) {
const updatedRow = {...this.items[data.index], [data.field.key]: value};
this.$set(this.items, data.index, updatedRow);
}
}
};
</script>
Form Elements:
Every column requires a type
and editable
property in order to make the cell editable:
[
{ key: "name", label: "Name", type: "text", editable: true},
{ key: "department", label: "Department", type: "select", options: ['Accounting', 'Marketing', 'Development', 'HR'], editable: true },
{ key: "age", label: "Age", type: "number", editable: true },
{ key: "dateOfBirth", label: "Date Of Birth", type: "date", editable: true },
{ key: "isActive", label: "Is Active", type: "checkbox", editable: true },
]
Elements' attributes and properties are supported by passing them directly through the field
object. For example, you can add size
and locale
props to the date picker as follows:
{ key: "dateOfBirth", label: "Date Of Birth", size:"lg", locale:"fr", type: "date", editable: true }
Supported Bootstrap form elements: |Type | Description | |--|--| | text | Bootstrap Form Text Input | number | Bootstrap Form Number Input | select | Bootstrap Form Select | date | Bootstrap Form Datepicker | checkbox | Bootstrap Form Checkbox | rating | Bootstrap Form Rating
Keyboard Keys:
|Key |Behavior | |--|--| | Tab | Move to the next cell | | Esc | Exit edit mode |
Column Width
To set the width of any column, you can pass the class
property in the fields
object. This feature is part of Bootstrap Vue Table. You can learn more from Bootstrap Table Docs.
Below is an example of how you set the width by adding a CSS class to the column:
fields: [
{ key: "name", label: "Name", type: "text", class: "name-col", editable: true, placeholder: "Enter Name..."},
{ key: "age", label: "Age", type:"range", class: "age-col", min:"0", max:"100", editable: true, placeholder: "Enter Age..." }
]
<style>
.name-col {
width: 120px;
}
.age-col {
width: 100px;
}
</style>
Custom Styling
There are no custom themes available yet but since it extends Bootstrap Vue Table, you can apply all options available from Bootstrap Table Docs as well as defining your own CSS class.
Here is an example of how to style and customize a table:
<template>
<div>
<b-editable-table bordered class="editable-table" v-model="items" :fields="fields" @input-change="handleInput">
<template #cell(isActive)="data">
<span v-if="data.value">Yes</span>
<span v-else>No</span>
</template>
</b-editable-table>
</div>
</template>
<script>
import BEditableTable from 'bootstrap-vue-editable-table';
export default {
components: {
BEditableTable
},
data() {
return {
fields: [
{ key: "name", label: "Name", type: "text", editable: true, placeholder: "Enter Name...", class: "name-col"},
{ key: "department", label: "Department", type: "select", editable: true, class: "department-col" , options: [
{ value: 1, text: 'HR' },
{ value: 2, text: 'Engineer' },
{ value: 3, text: 'VP' },
{ value: 4, text: 'CEO'}
]},
{ key: "age", label: "Age", type:"range", min:"0", max:"100", editable: true, placeholder: "Enter Age...", class: "age-col" },
{ key: "dateOfBirth", label: "Date Of Birth", type: "date", editable: true, class: "date-col", locale: "en",
"date-format-options": {
year: "numeric",
month: "numeric",
day: "numeric",
}, },
{ key: "isActive", label: "Is Active", type: "checkbox", editable: true, class: "is-active-col" }
],
items: [
{ age: 40, name: 'Dickerson', department: 1, dateOfBirth: '1984-05-20', isActive: true },
{ age: 21, name: 'Larsen', department: 2, dateOfBirth: '1972-07-25', isActive: false },
{ age: 89, name: 'Geneva', department: 3, dateOfBirth: '1981-02-02', isActive: false },
{ age: 38, name: 'Jami', department: 4, dateOfBirth: '1964-10-19', isActive: true },
]
};
},
methods: {
handleInput(value, data) {}
}
};
</script>
<style>
table.editable-table {
margin: auto;
}
table.editable-table td {
vertical-align: middle;
}
.editable-table .data-cell {
padding: 8px;
vertical-align: middle;
}
.editable-table .custom-checkbox {
width: 50px;
}
.name-col {
width: 120px;
}
.department-col {
width: 150px;
}
.age-col {
width: 100px;
}
.date-col {
width: 200px;
}
.is-active-col {
width: 100px
}
</style>
.data-cell
is an internal class used for the div
element within every non-editable cell. You can customize it however you like.
Events:
|Event |Arguments | Description |
|--|--|--|
| input-change |value
- Current cell value
data
- Row data (the same object returned by Bootstrap)| Emitted when any cell input changes
Edit Properties:
|Property |Options| Default | Description |
|--|--|--|--|
| editMode |cell
- Edit one cell
row
- Edit all the cells of a row at once| cell
| Change edit mode
| editTrigger|click
- Edit on mouse click
dblclick
- Edit on mouse double click| click
| Change edit trigger
Custom Cell
To customize a none editable cell, you can use Bootstraps' scoped slots.
Example rendering a boolean
field to Yes
or No
value:
<b-editable-table v-model="items" :fields="fields">
<template #cell(isActive)="data">
<span v-if="data.value">Yes</span>
<span v-else>No</span>
</template>
</b-editable-table>
For more details about custom slots, please read BootstrapVue Table documentation
Add and Remove Rows
<template>
<div class="table-container">
<b-button variant="success" @click="handleAdd()">Add</b-button>
<b-editable-table bordered class="editable-table" v-model="items" :fields="fields">
<template #cell(isActive)="data">
<span v-if="data.value">Yes</span>
<span v-else>No</span>
</template>
<template #cell(delete)="data">
<BIconX class="remove-icon" @click="handleDelete(data)"></BIconX>
</template>
</b-editable-table>
</div>
</template>
<script>
import BEditableTable from 'bootstrap-vue-editable-table';
import {BIconX} from 'bootstrap-vue';
export default {
components: {
BEditableTable
},
data() {
return {
fields: [
{ key: "name", label: "Name", type: "text", editable: true, placeholder: "Enter Name...", class: "name-col"},
{ key: "department", label: "Department", type: "select", editable: true, class: "department-col" , options: [
{ value: 1, text: 'HR' },
{ value: 2, text: 'Engineer' },
{ value: 3, text: 'VP' },
{ value: 4, text: 'CEO'}
]},
{ key: "age", label: "Age", type:"range", min:"0", max:"100", editable: true, placeholder: "Enter Age...", class: "age-col" },
{ key: "dateOfBirth", label: "Date Of Birth", type: "date", editable: true, class: "date-col", locale: "en",
"date-format-options": {
year: "numeric",
month: "numeric",
day: "numeric",
}, },
{ key: "isActive", label: "Is Active", type: "checkbox", editable: true, class: "is-active-col" },
{ key: "delete", label: "" }
],
items: [
{ age: 40, name: 'Dickerson', department: 1, dateOfBirth: '1984-05-20', isActive: true },
{ age: 21, name: 'Larsen', department: 2, dateOfBirth: '1972-07-25', isActive: false },
{ age: 89, name: 'Geneva', department: 3, dateOfBirth: '1981-02-02', isActive: false },
{ age: 38, name: 'Jami', department: 4, dateOfBirth: '1964-10-19', isActive: true },
]
};
},
methods: {
handleAdd() {
this.items.unshift({});
},
handleDelete(data) {
this.items.splice(data.index, 1);
}
}
};
</script>
<style>
.table-container {
margin: 10px;
}
table.editable-table {
margin-top: 10px;
}
table.editable-table td {
vertical-align: middle;
}
.editable-table .data-cell {
padding: 8px;
vertical-align: middle;
}
.editable-table .custom-checkbox {
width: 50px;
}
.remove-icon {
color: red;
cursor: pointer;
font-size: 20px;
}
.name-col {
width: 120px;
}
.department-col {
width: 150px;
}
.age-col {
width: 100px;
}
.date-col {
width: 200px;
}
.is-active-col {
width: 100px
}
</style>
Load Data via REST API
<template>
<div>
<b-editable-table :busy="loading" bordered class="editable-table" v-model="users" :fields="fields">
<template #cell(isActive)="data">
<span v-if="data.value">Yes</span>
<span v-else>No</span>
</template>
<template #table-busy>
<div class="text-center text-danger my-2">
<b-spinner class="align-middle"></b-spinner>
<strong>Loading...</strong>
</div>
</template>
</b-editable-table>
</div>
</template>
<script>
import BEditableTable from 'bootstrap-vue-editable-table';
import {BSpinner} from 'bootstrap-vue';
export default {
components: {
BEditableTable,
BSpinner
},
data() {
return {
fields: [
{ key: "name", label: "Name", type: "text", editable: true },
{ key: "email", label: "Email", type: "email", editable: true },
{ key: "phone", label: "Phone", type: "text", editable: true }
],
users: [],
loading: false
};
},
async mounted() {
this.loading = true;
const response = await fetch('https://jsonplaceholder.typicode.com/users');
const users = await response.json();
this.users = users;
this.loading = false;
}
};
</script>
<style>
table.editable-table {
margin: auto;
}
table.editable-table td {
vertical-align: middle;
}
.editable-table .data-cell {
padding: 8px;
vertical-align: middle;
}
.editable-table .custom-checkbox {
width: 50px;
}
.name-col {
width: 120px;
}
.department-col {
width: 150px;
}
.age-col {
width: 100px;
}
.date-col {
width: 200px;
}
.is-active-col {
width: 100px
}
</style>
Supported Version
This has been tested on:
- vue
v2.6.12
- bootstrap
v4.3.1
- bootstrap-vue
v2.21.2
We are looking to support as many versions as possible so please create an issue if you encounter compatibility issues 🙏
Roadmap
- Tabbing
- Two-way binding
- Support all bootstrap form elements
- Text
- Select
- Number
- Date
- Checkbox
- Rating
- Tags
- File upload
- Enable row editing (allows to edit all the cells of a row at once)
- Validation
- Vue 3 support
- Styling themes