Reputation: 1792
I am using tabulator to create tables on my website. I can select rows by clicking on them and I can select all rows using the tabulator.selectRow() method.
According to the documentation:
To select a specific row you can pass the any of the standard row component look up options into the first argument of the function.
I have seen this a few times but there is no working example. Does anybody know how the row component look up options need to be provided?
Let's say I have rows which have a name field.
I want to select the row where name == 'dennis'.
The documentation suggests that I can pass a lookup option in the selectRow() argument. There is just no example or any indication to expected syntax for the argument.
I currently have this working like so which does not seem to be most intuitive way.
table.getRows().forEach(row => {
if (row.getData().name == 'dennis') row.toggleSelect();
});
Or like so:
params.layer.tableView.table.getRows()
.filter(row => row.getData().name == 'dennis')
.forEach(row => row.toggleSelect());
Upvotes: 1
Views: 3865
Reputation: 444
Thank you for your post Dennis.
Here is a working example for Tabulator 6.2.
After a cell in the nested table is updated, the code updates other values in the same nested row, as well as in the parent row.
I added many comments to explain the functionality.
To test, run the following code in a browser and change any Qty value in a nested row:
<html>
<head>
<link href="https://unpkg.com/[email protected]/dist/css/tabulator.min.css" rel="stylesheet">
</head>
<body>
<div id="example-table"></div>
<script type="text/javascript" src="https://unpkg.com/[email protected]/dist/js/tabulator.min.js"></script>
<script>
//define some sample data, with the sub-table data held in the subtable parameter
var tableData = [
{
invoiceId: 1, customer: "Bob", invoiceTotal: "",
invoiceRows: [
{ itemName: "Bread", qty: 3, price: 2, itemTotal: "", invoiceId: 1 }
],
},
{
invoiceId: 2, customer: "Ann", invoiceTotal: "",
invoiceRows: [
{ itemName: "Tyres", qty: 4, price: 100, itemTotal: "", invoiceId: 2 },
{ itemName: "Gas", qty: 1, price: 30, itemTotal: "", invoiceId: 2 }
],
},
{
invoiceId: 3, customer: "Jim", invoiceTotal: "",
invoiceRows: [
{ itemName: "Wine", qty: 2, price: 4, itemTotal: "", invoiceId: 3 },
{ itemName: "Cheese", qty: 7, price: 3, itemTotal: "", invoiceId: 3 }
],
},
];
let table = new Tabulator("#example-table", {
height:400,
data:tableData,
layout:"fitDataFill",
columns:[
{ title: "Invoice Id", field:"invoiceId"},
{ title: "Customer", field: "customer" },
{ title: "Invoice Total", field: "invoiceTotal",
formatter: function (cell, formatterParams, onRendered) {
// Sum the values of the nested rows
let rowData = cell.getRow().getData();
let invoiceRows = rowData['invoiceRows'];
let varInvoiceCost = 0;
let itemCount = Object.keys(invoiceRows).length
for (let i = 0; i < itemCount; i++) {
let itemObj = invoiceRows[i];
let varQty = 0;
let varPrice = 0;
Object.entries(itemObj).forEach(([key, value]) => {
if (key == 'qty') {
varQty = value;
}
if (key == 'price') {
varPrice = value;
}
});
varInvoiceCost += (varQty * varPrice);
}
return varInvoiceCost;
},
},
],
rowFormatter: function (row) {
//create and style holder elements
var holderEl = document.createElement("div");
var tableEl = document.createElement("div");
// Remove nested table - if exists.
// Without this, the parent row will duplicate the
// nested table, each time you update the parent row.
holderEl.classList.add("nested-table")
var el = row.getElement();
var nested = el.getElementsByClassName("nested-table");
for (let child of nested) {
child.parentNode.removeChild(child);
}
holderEl.style.boxSizing = "border-box";
holderEl.style.padding = "10px 30px 10px 10px";
holderEl.style.borderTop = "1px solid #333";
holderEl.style.borderBotom = "1px solid #333";
tableEl.style.border = "1px solid #333";
holderEl.appendChild(tableEl);
row.getElement().appendChild(holderEl);
var subTable = new Tabulator(tableEl, {
layout: "fitColumns",
data: row.getData().invoiceRows,
columns: [
{ title: "Invoice Id", field: "invoiceId", }, // hide with visible:false,
{ title: "Item Name", field: "itemName" },
{ title: "Qty", field: "qty",
// Editor is used to edit the Qty value
editor: function (cell, onRendered, success, cancel, editorParams) {
let cellValue = cell.getValue();
let input = document.createElement("input");
input.setAttribute("type", "text");
input.value = typeof cellValue !== "undefined" ? cellValue : "";
onRendered(function () {
input.focus();
});
//when the value has been set, trigger the cell to update
function successFunc() {
// Update backend via Ajax if needed
// Call success(input.value) from Ajax.done()
success(input.value); // Update the screen
}
input.addEventListener("change", successFunc);
input.addEventListener("blur", successFunc);
return input;
},
// cellEdited is triggered after a Qty value was changed
cellEdited: function (cell) {
// Update the value of itemTotal in the nested row, which will
// trigger its formatter to recalculate and display
// its new value (based on the new Qty in the nested row).
let row = cell.getRow();
let varQty = row.getCell('qty').getValue();
let varPrice = row.getCell('price').getValue();
let varItemTotal = varQty * varPrice;
row.update({ "itemTotal": varItemTotal })
// Update the value of invoiceTotal in the parent row.
// Retrieve the parent row based on invoiceId in the nested row.
// Change the value of invoiceTotal in the parent row's DOM, which
// will trigger the invoiceTotal formatter to recalculate and display
// its new value (based on the new values in the nested rows).
// ***Note***
// Since the formatter will calculate the value to display,
// based on the values of the nested rows (not invoiceTotal),
// you could technically store any value in invoiceTotal,
// as long as its a different value than before (to trigger the
// formatter). To illustrate this, the following statements update
// the value of invoiceTotal with the current timestamp, but the
// formatter will still display the correct output (we could have
// done the same with itemTotal above).
// However, if you later retrieve the value of invoiceTotal, you
// will get the timestamp stored in the DOM, and not the value which
// the formatter returned (displayed on the screen).
// Ps. This is just to explain why console.log() sometimes outputs
// different values from what are displayed on your screen.
let varInvoiceId = row.getCell('invoiceId').getValue();
let parentRow = table.getRows().find(row => {
return row.getCell('invoiceId').getValue() == varInvoiceId;
});
parentRow.update({ "invoiceTotal": Date.now() }); // use timestamp
},
},
{ title: "Price", field: "price" },
{
title: "Item Total", field: "itemTotal",
formatter: function (cell, formatterParams, onRendered) {
let varQty = cell.getRow().getCell('qty').getValue();
let varPrice = cell.getRow().getCell('price').getValue();
return varQty * varPrice;
},
},
]
})
},
});
</script>
</body>
</html>
Upvotes: 0
Reputation: 1792
I got there in the end with help via github. #1749
The bit which cleared this up for me was this:
Any function that takes a component as an argument will also attempt to find that component based on the value provided if it is not a component itself.
Here as an example to select my row.
table.selectRow(table.getRows().filter(row => row.getData().name == 'Dennis'));
Upvotes: 4