Related
title pretty much says it, I have users that have different roles they can choose from and I need to toggle the row selection functionality on/off depending on the role they choose, is this possible or do you have to recreate the tabulator to change this value?
This option is not toggleable once the table has been instantiated, but you could use a Selection Eligibility function to determine if the row should currently be selectable:
var table = new Tabulator("#example-table", {
selectableCheck:function(row){
//row - row component
return row.getData().age > 18; //allow selection of rows where the age is greater than 18
},
});
You could tweak the above example to look at a global boolean that you toggle to determine if the table is selectable
In this I can select rows with name Oli Bob only See Documentation
Use as per your convinience
selectableCheck:function(row){
//row - row component
return row.getData().age > 18; //allow selection of rows where the age is greater than 18
},
const tabledata = [{
name: "Oli Bob",
location: "United Kingdom",
gender: "male",
col: "red",
dob: "14/04/1984"
},
{
name: "Oli Bob",
location: "United Kingdom",
gender: "male",
col: "red",
dob: "14/04/1984"
},
{
name: "Jamie Newhart",
location: "India",
gender: "male",
col: "green",
dob: "14/05/1985"
}
];
let selectable = false;
const table = new Tabulator("#example-table", {
data: tabledata,
selectable: true,
selectableCheck: function(row) {
const name = row.getData().name;
return name === "Oli Bob";
},
columns: [{
title: "Row Num",
formatter: "rownum"
},
{
title: "Name",
field: "name",
width: 200
},
],
});
<script src="https://unpkg.com/tabulator-tables#4.4.3/dist/js/tabulator.min.js"></script>
<link href="https://unpkg.com/tabulator-tables#4.4.3/dist/css/tabulator.min.css" rel="stylesheet" />
<div id="example-table"></div>
I'm trying to bind a custom keys combination to perform a custom action like below but the key binding is not working. I even tried adding keybindings: true in the init settings but no change. There are no errors, warnings or notices in the console whatsoever.
I'm really not sure whether I'm using the .extendModule() correctly, though I have checked the docs already.
var rowContextMenu = function(e, row) {
// do something here
console.log("RIGHT CLICK: Context menu triggered");
}
var deleteRow = function(row) {
let rData = row.getData();
if(rData.hasOwnProperty('_children')) {
let cRows = row.getTreeChildren();
$.each(cRows, function() {
let r = this;
setTimeout(function() {
deleteRow(r);
}, 0);
});
}
row.delete();
};
Tabulator.prototype.extendModule("keybindings", "actions", {
"deleteSelectedRows":function(){ //delete selected rows
let selectedRows = this.table.getSelectedRows();
selectedRows.forEach(function(row){
deleteRow(row);
});
console.log('ROWS DELETED: Triggered with button keyboard');
},
});
Tabulator.prototype.extendModule("keybindings", "bindings", {
deleteSelectedRows:"shift + 9",
});
let nestedData = [
{name:"Oli Bob", location:"United Kingdom", gender:"male", col:"red", dob:"14/04/1984", _children:[
{name:"Mary May", location:"Germany", gender:"female", col:"blue", dob:"14/05/1982"},
{name:"Christine Lobowski", location:"France", gender:"female", col:"green", dob:"22/05/1982"},
{name:"Brendon Philips", location:"USA", gender:"male", col:"orange", dob:"01/08/1980", _children:[
{name:"Margret Marmajuke", location:"Canada", gender:"female", col:"yellow", dob:"31/01/1999"},
{name:"Frank Harbours", location:"Russia", gender:"male", col:"red", dob:"12/05/1966"},
]},
]},
{name:"Jamie Newhart", location:"India", gender:"male", col:"green", dob:"14/05/1985"},
{name:"Gemma Jane", location:"China", gender:"female", col:"red", dob:"22/05/1982", _children:[
{name:"Emily Sykes", location:"South Korea", gender:"female", col:"maroon", dob:"11/11/1970"},
]},
{name:"James Newman", location:"Japan", gender:"male", col:"red", dob:"22/03/1998"},
];
let table = new Tabulator('#my-tabulator', {
height:"400px",
layout:"fitColumns",
data: nestedData,
/**
* // Actual ajax configuration that won't work with test data (works perfectly with live data)
*
* ajaxLoaderLoading:'<div class="text-center" style="display:inline-block;"><img style="width:100px;" src="/path/to/my/spinner.svg"></div>',
* ajaxURL:"/path/to/my/data.json", //ajax URL
* ajaxConfig:"get", //ajax HTTP request type
*/
sortable: false,
selectable:true, //make rows selectable
dataTree:true,
dataTreeChildIndent:24,
dataTreeStartExpanded:[true, false], //start with first level expanded, second level collapsed
dataTreeCollapseElement:"<span class='expand-collpase-btn'>-</span>",
dataTreeExpandElement:"<span class='expand-collpase-btn'>+</span>",
rowFormatter:function(row){
if(row.getData().parent_id === null){
row.getElement().classList.add("root-node");
}
},
keybindings: {
deleteSelectedRows:"shift + 9"
},
rowContext:rowContextMenu,
columnMinWidth : 24,
columns:[
{title:"Name", field:"name", width:200, responsive:0}, //never hide this column
{title:"Location", field:"location", width:150},
{title:"Gender", field:"gender", width:150, responsive:2}, //hide this column first
{title:"Favourite Color", field:"col", width:150},
{title:"Date Of Birth", field:"dob", align:"center", sorter:"date", width:150},
],
/**
* // Actual column configuration that won't work with test data (works perfectly with live data)
*
* columns:[
* {title:"Col 1", field:"field_1", headerSort:false, titleFormatter:headerFilter, titleFormatterParams:filters, responsive:0,
* formatter:function(cell, formatterParams){
* //cell - the cell component
* //formatterParams - parameters set for the column
*
* let rowData = cell.getRow().getData();
*
* if(rowData['test'] == 'String 1' || rowData['test'] === false) {
* let cellUrl = $('<a></a>', {href: '/path/to/cell/item/itemid:'+rowData['id'], target: "_blank"}).html(cell.getValue());
* return cellUrl[0];
* }
* return cell.getValue(); //return the contents of the cell;
* },
* },
* {title:"Col 2", field:"field_2", headerSort:false, width:150,
* formatter:function(cell, formatterParams){
* //cell - the cell component
* //formatterParams - parameters set for the column
*
* let rowData = cell.getRow().getData();
*
* if(rowData['test']) {
* return '<span class="text-muted text-bold text-italic">String 1</span>';
* }
* return '<span class="text-italic">String 2</span>';
* },
* },
* {title:"Col 3", headerSort:false, field:"field_3", width:200, align:"right", titleFormatter:headerFilter, titleFormatterParams:filters, responsive:2}, //hide this column first
* {title:"Col 4", headerSort:false, field:"field_4", width:200, align:"right", titleFormatter:headerFilter, titleFormatterParams:filters},
* {
* title:"",
* headerSort:false,
* resizable: false,
* columns: [
* {title:"", width:24, headerSort:false, resizable: false, formatter:editButton, cssClass:'table-actions'},
* {title:"", width:24, headerSort:false, resizable: false, formatter:deleteButton, cssClass:'table-actions'},
* ]
* },
* ]
*/
});
$('#delete-rows-btn').on('click', function(e){
let selectedRows = table.getSelectedRows();
selectedRows.forEach(function(row){
deleteRow(row);
});
console.log('ROWS DELETED: Triggered with button click');
});
$('#reload-table-btn').on('click', function(e){
console.log('Table reloaded');
table.replaceData(nestedData);
});
/**
* I'm using custom CSS
* but let's ignore it for the sake of this demo
*/
<link href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://unpkg.com/tabulator-tables#4.2.7/dist/css/tabulator.min.css" rel="stylesheet"/>
<link href="https://unpkg.com/tabulator-tables#4.2.7/dist/css/bootstrap/tabulator_bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
<script src="https://unpkg.com/tabulator-tables#4.2.7/dist/js/tabulator.min.js"></script>
<a id="delete-rows-btn" href="javascript:void(0);" class="btn btn-sm btn-danger">Delete Selected</a>
<a id="reload-table-btn" href="javascript:void(0);" class="btn btn-sm btn-primary">Reload Table</a>
<div id="my-tabulator" class="table-bordered"></div>
you need to use shift + keyCodeand disable rowSelectable. My code works if you
focus on table
press Shift + 9 Key
var rowContextMenu = function (e, row) {
// do something here
console.log("RIGHT CLICK: Context menu triggered");
}
var deleteRow = function (row) {
let rData = row.getData();
if (rData.hasOwnProperty('_children')) {
let cRows = row.getTreeChildren();
$.each(cRows, function () {
let r = this;
setTimeout(function () {
deleteRow(r);
}, 0);
});
}
row.delete();
};
Tabulator.prototype.extendModule("keybindings", "actions", {
"deleteSelectedRows": function () { //delete selected rows
let selectedRows = this.table.getSelectedRows();
selectedRows.forEach(function (row) {
deleteRow(row);
});
console.log('ROWS DELETED: Triggered with button keyboard');
},
});
Tabulator.prototype.extendModule("keybindings", "bindings", {
deleteSelectedRows: "shift + 9",
});
let nestedData = [
{
name: "Oli Bob", location: "United Kingdom", gender: "male", col: "red", dob: "14/04/1984", _children: [
{name: "Mary May", location: "Germany", gender: "female", col: "blue", dob: "14/05/1982"},
{name: "Christine Lobowski", location: "France", gender: "female", col: "green", dob: "22/05/1982"},
{
name: "Brendon Philips", location: "USA", gender: "male", col: "orange", dob: "01/08/1980", _children: [
{name: "Margret Marmajuke", location: "Canada", gender: "female", col: "yellow", dob: "31/01/1999"},
{name: "Frank Harbours", location: "Russia", gender: "male", col: "red", dob: "12/05/1966"},
]
},
]
},
{name: "Jamie Newhart", location: "India", gender: "male", col: "green", dob: "14/05/1985"},
{
name: "Gemma Jane", location: "China", gender: "female", col: "red", dob: "22/05/1982", _children: [
{name: "Emily Sykes", location: "South Korea", gender: "female", col: "maroon", dob: "11/11/1970"},
]
},
{name: "James Newman", location: "Japan", gender: "male", col: "red", dob: "22/03/1998"},
];
let table = new Tabulator('#my-tabulator', {
height: "400px",
layout: "fitColumns",
data: nestedData,
sortable: false,
// selectable: true, //make rows selectable
dataTree: true,
dataTreeChildIndent: 24,
dataTreeStartExpanded: [true, false], //start with first level expanded, second level collapsed
dataTreeCollapseElement: "<span class='expand-collpase-btn'>-</span>",
dataTreeExpandElement: "<span class='expand-collpase-btn'>+</span>",
rowFormatter: function (row) {
if (row.getData().parent_id === null) {
row.getElement().classList.add("root-node");
}
},
keybindings: {
deleteSelectedRows: "shift + 57"
},
rowContext: rowContextMenu,
columnMinWidth: 24,
columns: [
{title: "Name", field: "name", width: 200, responsive: 0}, //never hide this column
{title: "Location", field: "location", width: 150},
{title: "Gender", field: "gender", width: 150, responsive: 2}, //hide this column first
{title: "Favourite Color", field: "col", width: 150},
{title: "Date Of Birth", field: "dob", align: "center", sorter: "date", width: 150},
],
});
$('#delete-rows-btn').on('click', function (e) {
let selectedRows = table.getSelectedRows();
selectedRows.forEach(function (row) {
deleteRow(row);
});
console.log('ROWS DELETED: Triggered with button click');
});
$('#reload-table-btn').on('click', function (e) {
console.log('Table reloaded');
table.replaceData(nestedData);
});
<link href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://unpkg.com/tabulator-tables#4.2.7/dist/css/tabulator.min.css" rel="stylesheet"/>
<link href="https://unpkg.com/tabulator-tables#4.2.7/dist/css/bootstrap/tabulator_bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
<script src="https://unpkg.com/tabulator-tables#4.2.7/dist/js/tabulator.min.js"></script>
<a id="delete-rows-btn" href="javascript:void(0);" class="btn btn-sm btn-danger">Delete Selected</a>
<a id="reload-table-btn" href="javascript:void(0);" class="btn btn-sm btn-primary">Reload Table</a>
<div id="my-tabulator" class="table-bordered"></div>
I have a column which is formatted using the built in money formatter. I would like to change the text of the cell to red if the numeric value of the cell is negative. I can't create a custom formatter because the column formatter option is already set to money.
You have to use a custom formatter, Mimic the money formatter in this code. I can't think of anything else
let tabledata = [{
id: 1,
name: "Oli ",
money: 1,
col: "red",
dob: ""
},
{
id: 2,
name: "Mary ",
money: -1,
col: "blue",
dob: "14/05/1982"
},
{
id: 3,
name: "Christine ",
money: 0,
col: "green",
dob: "22/05/1982"
},
{
id: 4,
name: "Brendon ",
money: 10,
col: "orange",
dob: "01/08/1980"
},
{
id: 5,
name: "Margret ",
money: -10,
col: "yellow",
dob: "31/01/1999"
},
];
for (let i = 0; i < tabledata.length; i++) {
if (tabledata[i].money < 0) {
tabledata[i].money = "<span class='red'>$" +
tabledata[i].money +
"</span>"
} else {
tabledata[i].money = '$' + tabledata[i].money;
}
}
const table = new Tabulator("#example-table", {
data: tabledata,
layout: "fitColumns",
columns: [{
title: "id",
field: "id"
},
{
title: "name",
field: "name"
},
{
title: "money",
field: "money",
formatter: "html"
},
{
title: "col",
field: "col"
},
{
title: "dob",
field: "dob"
},
]
});
.red {
color: red;
}
<!DOCTYPE html>
<html lang="en">
<script src="https://unpkg.com/tabulator-tables#4.2.4/dist/js/tabulator.min.js"></script>
<link href="https://unpkg.com/tabulator-tables#4.2.4/dist/css/tabulator.min.css" rel="stylesheet" />
<body>
<div id="example-table"></div>
</body>
</html>
I think I found a solution for this problem:
formatter:function(cell,params,callback) {
let money = cell.getTable().modules.format.getFormatter("money");
cell.getElement().style...
return money(cell,params,callback);
}
https://jsfiddle.net/baumrock/vxpbhLsg/14/
Even in the example given # the Tabulator site:
http://tabulator.info/basic/4.2
the date column does not sort, unlike the others. Is there a fix for that?
Date sorting for Tabulator is dependent on MomentJS.
Import Moment Js to your code
{title: "Date Of Birth", field: "dob", sorter:"date", sorterParams:{format:"DD/MM/YY"}},
Please check this Snippet
const tabledata1 = [
{id: 1, name: "Oli ", money: "0", col: "red", dob: "14/05/1982"},
{id: 2, name: "Mary ", money: "0", col: "blue", dob: "14/05/1982"},
{id: 3, name: "Christine ", money: "0", col: "green", dob: "22/05/1982"},
{id: 4, name: "Brendon ", money: "0", col: "orange", dob: "01/08/1980"},
{id: 5, name: "Margret ", money: "0", col: "yellow", dob: "31/01/1999"},
];
const table = new Tabulator("#example-table", {
height: 205, // set height of table (in CSS or here), this enables the Virtual DOM and improves render speed dramatically (can be any valid css height value)
data: tabledata1, //assign data to table
layout: "fitColumns", //fit columns to width of table (optional)
columns: [ //Define Table Columns
{title: "Name", field: "name", width: 150},
{
title: "money",
field: "money",
align: "left",
formatter: "money"
},
{title: "Favourite Color", field: "col"},
{title: "Date Of Birth", field: "dob", sorter:"date", sorterParams:{format:"DD/MM/YY"}},
]
});
function removeData() {
table.clearData();
}
function update() {
table.updateOrAddData(tabledata2);
// table.addData(tabledata2);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
<script src="https://unpkg.com/tabulator-tables#4.2.4/dist/js/tabulator.min.js"></script>
<link href="https://unpkg.com/tabulator-tables#4.2.4/dist/css/tabulator.min.css" rel="stylesheet"/>
<script src="https://code.jquery.com/jquery-3.4.0.min.js"></script>
<div id="example-table"></div>
The call below with the matching result set returns a result that does not start with the doc id a4a6cf44-8a82-494a-a2b9-f6a3ec629f17. As the result set below demonstrates the top 3 keys are identical, but startkey_docid has no effect.
Just a couple questions around this
Will startkey_docid work correctly with a complex key? (This apparently works for couchdb)
If yes to the above, is the source of the issue the use of dateToArray(doc.created) in our view?
Is this a bug?
View:
function (doc, meta)
{
if(meta.type == "json" && doc.type == "POST")
{
emit([doc.category, dateToArray(doc.created), doc.visibility], null);
}
}
Call:
?startkey=["auto",[2013,10,10,23,12,0],"EVERYONE"]&endkey=["auto",[2013,12,11,23,12,0],"EVERYONE"]&startkey_docid=a4a6cf44-8a82-494a-a2b9-f6a3ec629f17
Result:
{
total_rows: 20,
rows: [{
id: "a4a6cf44-8a82",
key: ["auto", [2013, 11, 8, 1, 17, 46], "EVERYONE"],
value: null
}, {
id: "a4a6cf44-8a82-494a-a2b9",
key: ["auto", [2013, 11, 8, 1, 17, 46], "EVERYONE"],
value: null
}, {
id: "a4a6cf44-8a82-494a-a2b9-f6a3ec629f17",
key: ["auto", [2013, 11, 8, 1, 17, 46], "EVERYONE"],
value: null
}, {
id: "41070cfc-a85c-424c-9b87-fce0616c77c1",
key: ["auto", [
2013, 11, 11, 20, 28, 21], "EVERYONE"],
value: null
}, {
id: "bb275e3c-54da-4e85-8cc3-21defff4e278",
key: ["auto", [
2013, 11, 13, 1, 41, 7], "EVERYONE"],
value: null
}]
}
Example data set. post_id is the document id.
{
type: "POST",
post_id: "a4a6cf44-8a82",
visibility: "EVERYONE",
userID: "<user_id>",
title: "Some title 1",
category: "auto",
description: "",
created: "2013-11-07 17:17:46 -0800",
modified: "2013-11-07 17:17:46 -0800"
}, {
type: "POST",
post_id: "a4a6cf44-8a82-494a-a2b9",
visibility: "EVERYONE",
userID: "<user_id>",
title: "Some title 2",
category: "auto",
description: "",
created: "2013-11-07 17:17:46 -0800",
modified: "2013-11-07 17:17:46 -0800"
}, {
type: "POST",
post_id: "a4a6cf44-8a82-494a-a2b9-f6a3ec629f17",
visibility: "EVERYONE",
userID: "<user_id>",
title: "Some title 3",
category: "auto",
description: "",
created: "2013-11-07 17:17:46 -0800",
modified: "2013-11-07 17:17:46 -0800"
}, {
type: "POST",
post_id: "41070cfc-a85c-424c-9b87-fce0616c77c1",
visibility: "EVERYONE",
userID: "<user_id>",
title: "Some title 4",
category: "auto",
description: "",
created: "2013-11-11 12:28:21 -0800",
modified: "2013-11-11 12:28:21 -0800"
}, {
type: "POST",
post_id: "bb275e3c-54da-4e85-8cc3-21defff4e278",
visibility: "EVERYONE",
userID: "<user_id>",
title: "Some title 5",
category: "auto",
description: "",
created: "2013-11-12 17:41:07 -0800",
modified: "2013-11-12 17:41:07 -0800"
}
Okay, this is an old thread, but for reference sake, I'll post my findings and try to explain this functionality the best I can.
Example
Given a database of over 9,000 movie documents, each containing attributes like title, year, rating, genre, etc., define a view MovieIndexView with index name MovieIndex for this database that emits the following complex (but rather simple) key for filtering movies by rating, and sorting by year:
function (doc) {
emit([doc.rating, doc.year], doc);
}
The view can be queried with sorted rows using techniques similar to the way the OP has shown:
- Request -
GET /movies_db/_design/MovieIndex/_view/MovieIndexView?
startkey=["R",{}]
&endkey=["R",0]
&descending=true
&limit=6
- Response -
{
"total_rows": 9411,
"offset": 1,
"rows": [
{
"id": "2802144",
"key": [
"R",
2014
],
"value": {
"_id": "2802144",
"_rev": "1-924e12ba1f1144e3a453bbd8978acc5c",
"title": "Kingsman: The Secret Service",
"year": 2014,
"rating": "R",
"runtime": "129 min",
"genre": [
"Action",
"Adventure",
"Comedy"
],
"director": "Matthew Vaughn"
}
},
<...>
{
"id": "2278388",
"key": [
"R",
2014
],
"value": {
"_id": "2278388",
"_rev": "1-c38b7f5eb43abfd59fb8514277290e46",
"title": "The Grand Budapest Hotel",
"year": 2014,
"rating": "R",
"runtime": "99 min",
"genre": [
"Adventure",
"Comedy",
"Drama"
],
"director": "Wes Anderson"
}
}
]
}
Note we only pulled the first 6 documents by setting the limit variable (I pasted only the first and last relevant items).
Pagination
A first shot at pagination for the next sequence of rows could be straight forward. Simply set the startkey of the next request to the key value of the last item in the previous set, while keeping the value for endkey unchanged:
startkey=["R",2014]
&endkey=["R",0]
&descending=true
&limit=6
Do you see a problem with this approach? This new start key fails to specify which of the multiple movie items with the same value for startkey (movies with the same rating, that came out the same year) the response should start with. While there is nothing syntactically wrong with the request, you will notice that the returned rows will not start with the next sequential item we want, but rather with another arbitrary item in the database with a matching value for key.
This is where startkey_docid comes in handy:
- Request -
GET /movies_db/_design/MovieIndexView/_view/MovieIndex?
startkey=["R",2014]
&endkey=["R",0]
&descending=true
&limit=6
&startkey_docid=2278388
This request will return rows starting with the correct item in the sequence, providing the best means of paginating the rows from the view.
The Takeaway
A startkey_docid must be used together with a relevant startkey for your pagination to work.
I hope this helps anyone who may be busting their heads on this subtle feature.
Sources
CouchDB Official Docs: http://docs.couchdb.org/en/latest/couchapp/views/collation.html
Interesting thread exchange: http://grokbase.com/t/couchdb/user/091defx51x/sort-by-date-and-find-by-key
Similar SO topic: https://stackoverflow.com/questions/10142850/need-a-couchdb-trick-to-sort-by-date-and-filter-by-group#=
You can find more information on how to use startkey_docid properly here: http://blog.couchbase.com/startkeydocid-behaviour