Show/Hide or Toggle Nested Table Child In Tabulator - tabulator

I was wondering if you could help with something I believe to be pretty simple. Using the Tabulator nested table example(Not Tree), how can I make the child table show/hide on click? I want users to be able to expand for further information if they require it similar to the tree example.
I have seen a few answers to this but they don't seem to work for me.
//define table
var table = new Tabulator("#example-table", {
height:"311px",
layout:"fitColumns",
resizableColumns:false,
data:nestedData,
columns:[
{title:"Make", field:"make"},
{title:"Model", field:"model"},
{title:"Registration", field:"reg"},
{title:"Color", field:"color"},
],
rowFormatter:function(row){
//create and style holder elements
var holderEl = document.createElement("div");
var tableEl = document.createElement("div");
holderEl.style.boxSizing = "border-box";
holderEl.style.padding = "10px 30px 10px 10px";
holderEl.style.borderTop = "1px solid #333";
holderEl.style.borderBotom = "1px solid #333";
holderEl.style.background = "#ddd";
tableEl.style.border = "1px solid #333";
holderEl.appendChild(tableEl);
row.getElement().appendChild(holderEl);
var subTable = new Tabulator(tableEl, {
layout:"fitColumns",
data:row.getData().serviceHistory,
columns:[
{title:"Date", field:"date", sorter:"date"},
{title:"Engineer", field:"engineer"},
{title:"Action", field:"actions"},
]
})
},
});

Using a mix of #dota2pro example here is a nice working solution:
https://jsfiddle.net/ustvnz5a/2/
var nestedData = [{
id: 1,
make: "Ford",
model: "focus",
reg: "P232 NJP",
color: "white",
serviceHistory: [{
date: "01/02/2016",
engineer: "Steve Boberson",
actions: "Changed oli filter"
},
{
date: "07/02/2017",
engineer: "Martin Stevenson",
actions: "Break light broken"
},
]
},
{
id: 2,
make: "BMW",
model: "m3",
reg: "W342 SEF",
color: "red",
serviceHistory: [{
date: "22/05/2017",
engineer: "Jimmy Brown",
actions: "Aligned wheels"
},
{
date: "11/02/2018",
engineer: "Lotty Ferberson",
actions: "Changed Oil"
},
{
date: "04/04/2018",
engineer: "Franco Martinez",
actions: "Fixed Tracking"
},
]
},
]
var hideIcon = function(cell, formatterParams, onRendered){ //plain text value
return "<i class='fa fa-eye-slash'></i>";
};
const table = new Tabulator("#example-table", {
height: "311px",
layout: "fitColumns",
resizableColumns: false,
data: nestedData,
selectable: true,
columns: [{
title: "Make",
field: "make"
},
{
title: "Model",
field: "model"
},
{
title: "Registration",
field: "reg"
},
{
title: "Color",
field: "color"
},
{formatter:hideIcon, align:"center", title:"Hide Sub", headerSort:false, cellClick:function(e, row, formatterParams){
const id = row.getData().id;
$(".subTable" + id + "").toggle();
}
}
],
rowFormatter: function(row, e) {
//create and style holder elements
var holderEl = document.createElement("div");
var tableEl = document.createElement("div");
const id = row.getData().id;
holderEl.style.boxSizing = "border-box";
holderEl.style.padding = "10px 10px 10px 10px";
holderEl.style.borderTop = "1px solid #333";
holderEl.style.borderBotom = "1px solid #333";
holderEl.style.background = "#ddd";
holderEl.setAttribute('class', "subTable" + id + "");
tableEl.style.border = "1px solid #333";
tableEl.setAttribute('class', "subTable" + id + "");
holderEl.appendChild(tableEl);
row.getElement().appendChild(holderEl);
var subTable = new Tabulator(tableEl, {
layout: "fitColumns",
data: row.getData().serviceHistory,
columns: [{
title: "Date",
field: "date",
sorter: "date"
},
{
title: "Engineer",
field: "engineer"
},
{
title: "Action",
field: "actions"
},
]
})
},
});

Check this jsfiddle
selectable: true,
rowClick: function(e, row) {
const id = row.getData().id;
$(".subTable" + id + "").toggle();
},

This is just for help with hiding the expanded sections by default. My solution has 2 parts. One when I load the data and one when the page changes using the pagination controls.
Here is my part when I set the data:
myTable.setData(myData);
myTable.setHeight("100%");
myTable.redraw();
var rows = myTable.getRows();
rows.forEach(function (row) {
var data2 = row.getData();
const id = data2.IDField;
$(".subTable" + id + "").toggle();
});
I use this event to handle page changes:
pageLoaded: function (pageno) {
var rows = myTable.getRows();
rows.forEach(function (row) {
var data = row.getData();
const id = data.AssetNumber;
//use hide, toggle works great first time you view the tab, 2nd time it opens them all
$(".subTable" + id + "").hide();
//$(".subTable" + id + "").toggle();
});
}
Thanks for the great code used everyone else.

Related

Why is the Tabulator getRows() function not working?

This seems like the simplest of requests but I can't seem to retrieve a set of rows from a Tabulator object.
Here's the code which instantiates the Tabulator object.........
function TabulatorInvitees(divId, companyName, userEmail) {
try {
var table = new Tabulator(divId, {
columns: [
{
title: "<div style='width:20%; float:left; text-align:left; color:blue; font-size:14px;'>Vendor Invitees</div>",
columns: [
{ title: "Id", field: "Id", visible: false },
{ title: "Added", field: "Added", visible: false },
{ title: "Changed", field: "Changed", visible: false },
{ title: "MarkedForExclusion", field: "MarkedForExclusion", visible: false },
{ title: "Email Address", field: "Email", widthGrow: 1, responsive: 0, hozAlign: "center", editor: "input", visible: true },
{ title: "First Name", field: "FirstName", widthGrow: 0.5, responsive: 1, hozAlign: "center", editor: "input", visible: true },
{ title: "Last Name", field: "LastName", widthGrow: 0.5, responsive: 1, hozAlign: "center", editor: "input", visible: true }
]
},
{
title: tabulatorAddUser(companyName),
field: "ManageRows",
widthGrow: 0.25,
responsive: 2,
hozAlign: "center",
formatter: "tickCross",
headerClick: function (e, row) {
row.getTable().addRow({ Id: 0, Added: true }, false);
},
cellClick: function (e, cell) {
tabulatorFreezeUnfreezeDelete(cell.getRow());
}
},
],
data: [],
height: "100%",
layout: "fitColumns", // required when using 'widthGrow'
placeholder: tabulatorPlaceholder(companyName), //display message to user on empty table
reactiveData: true, //enable reactive data
responsiveLayout: "collapse",
rowContextMenu: tabulatorContextMenu(),
});
table.on("rowTapHold", function (e, row) {
// from Tabulator documentation: "The rowTapHold event is triggered when a user taps on a row on a touch display and holds their finger down for over 1 second."
//e - the tap event object
//row - row component
tabulatorContextMenu();
});
table.on("tableBuilt", function () {
if (companyName.length > 0) {
table.setData(getDataSync({ caseSelector: "VendorMgmt_EmployeeList", companyCode: companyName, userEmail: userEmail }));
}
else {
table.setData([]);
}
});
}
catch (error) {
console.log(error);
}
}
The setData() function makes a call to a database function which returns three rows, similar to the following:
The following JQuery function is called when a radio button is clicked....
$(".vendorStatus").click(function (e) {
const status = e.target.value;
const tbls = Tabulator.findTable("#divVendorEmployees");
const tbl = tbls[0];
const tblRows = tbl.getRows();
console.log("tbls.length", tbls.length);
console.log("tblRows", tblRows);
});
The browser console indicates a table has been found (tbls.length = 1) but the tblRows array is empty:
I see the three rows in my Tabulator but I am not able to recall them programmatically. It seems like a simple problem which should have a simple answer.
I am using the most recent version of Tabulator (v5.4).
Any assistance is greatly appreciated.
After much searching, I finally came to the realization the DOM element associated with the Tabulator instance must be managed when attempting to refresh or replace data. I've implemented a method which allows me to delete and rebuild the DOM element each time I need to save data to my database and subsequently refresh my Tabulator instance.
Here's the code...
function refreshTabulatorObject(obj) {
let parentId = obj.parentId;
let childId = obj.childId;
//Empty and remove the current version of the [Tabulator] object.
const tables = Tabulator.findTable(childId);
if (tables.length > 0) {
var table = Tabulator.findTable(childId)[0];
table.setData([]);
}
//Remove the existing <div> from the DOM.
$(childId).remove();
//Re-create the <div> element for the [Tabulator] object and append it to the DOM.
var parentDiv = document.getElementById(parentId);
parentDiv.innerHTML = "";
var childDiv = document.createElement("div");
childDiv.setAttribute("id", childId);
parentDiv.appendChild(childDiv);
//Re-create the [Tabulator] object.
TabulatorInvitees("#" + childId, obj.companyName);
}
I'm sure those of you with a more intimate knowledge of Tabulator would likely suggest a more elegant method, however, this one appears to work and I've already spent far more time on this issue that I had anticipated. Unfortunately, elegance is not a luxury I can afford at this point.
I hope this solution might be of help to some other struggling soul.

How do I programmatically change a cell value for a programmatically inserted row in Tabulator?

I have figured out how to programmatically copy and insert a new row in my Tabulator table utilizing a right-click and the rowContextMenu property; however I am having difficulty programmatically modifying data within the cells of the newly inserted row.
An abbreviated version of my Tabulator table setup code is as follows:
function TabulatorTimeSheet(divId) {
try {
var myActionContextMenu = [
{
label: "Copy & Paste Row",
action: function (e, row) {
var myTable = row.getTable();
var rowData = row.getData();
var idx = 0;
myTable.addData(rowData, false)
.then(function (newRows) {
//NOTE: The [Added] column of the new row needs to be set to "true".
newRows.forEach(newRow => {
idx = newRow.getPosition(true);
myTable.updateRow(idx, { Added: "true" });
});
myTable.redraw(true);
})
}
}
]
var table = new Tabulator(divId, {
height: "100%",
data: [],
layout: "fitDataFill",
selectable: 1,
cellEdited: function (cell) {
var myTable = cell.getTable();
var row = cell.getRow();
var rowData = row.getData();
rowData["Changed"] = "true";
},
rowContextMenu: myActionContextMenu,
columns: [
{ title: "Date Worked", field: "DateComp", responsive: 0, hozAlign: "center", sorter: "date", editor: dateEditor },
{ title: "Start Time", field: "TimeStart", responsive: 0, hozAlign: "center", sorter: "time", editor: timeEditor },
{ title: "Finish Time", field: "TimeFinish", responsive: 0, hozAlign: "center", sorter: "time", editor: timeEditor },
{ title: "Added", field: "Added", visible: true, responsive: 0, hozAlign: "center" },
{ title: "Changed", field: "Changed", visible: false, responsive: 0, hozAlign: "center" } ]
});
table.setData(myEndPointURL, { caseSelector: myCaseSelector, emplId: myEmployeeId });
}
catch (error) {
console.log(error);
}
The addData() function inserts a copy of the selected row, as expected; however, I am unable to modify the Added cell of the newly inserted row. Regardless of my efforts, I still see a blank cell.
Any assistance is greatly appreciated.
Most of your setup looks fine. Instead of doing myTable.updateRow, use newRows.update({field: 'new val'}). Because you already have the row reference, you don't need to get the id. I think that will solve your issue.
A few other things.
You are only inserting one row, so instead of myTable.addData, use myTable.addRow. Then, the return value will only be 1 row, so you don't need the forEach.
The table.redraw probably isn't necessary.
Here is a very simple example that should do all that you need it to. I omitted the context menu as it isn't relevant to the issue, but you just need to move the function into there and it should be good.
https://jsfiddle.net/nrayburn/65cngder/6/

jTable dynamic custom toolbar item in child table

I have a jTable with a child table for each row. On the toolbar header of the child table I have added a custom toolbar item. I want to make that toolbar item dynamic in the sense that if there are already some rows I do not want it to show. I came across a very similar query for the main toolbar "add new" button which added a function to run on recordsLoaded:
Below is my first attempt - it is just the field entry for the main table that specifies the child table. However the ".find(....)" spec will not work in my case as mine is a custom toolbar item. What do I need to put as the .find criteria?
Thanks
Dance: {
title: '',
width: '4%',
sorting: false,
create: false,
listClass: 'centreCol',
display: function(book) {
var $img = $('<img src="Images/layers.png" title="Show associated dance entries" />');
//Open child table when user clicks the image
$img.click(function() {
var thisrow = $img.closest('tr'); //Parent row
if($('#BookTableContainer').jtable('isChildRowOpen',thisrow)) { // Clicking image a second time closes the child row
$('#BookTableContainer').jtable('closeChildRow',thisrow);
} else {
currentTitleID = book.record.DanceTitleID;
$('#BookTableContainer').jtable(
'openChildTable',
thisrow,
{
title: 'Related Dance',
toolbar: {
items: [
{
icon: 'Images/add.png',
text: 'New dance',
tooltip: 'Add dance details',
click: function() { CreateDanceDialog(); }
}
]
},
actions: {
listAction: 'BookPageData.php?action=listChildDances&DanceTitleID=' + currentTitleID,
// createAction: 'dancesData.php?action=createAssignment',
// deleteAction: 'dancesData.php?action=deleteAssignment'
},
recordsLoaded: function(event, data) {
var rowCount = data.records.length;
if (rowCount>0){
$('#BookTableContainer').find('.jtable-toolbar-item.jtable-toolbar-item-add-record').remove();
}
},
fields: {
DanceID: { key: true, create: false, edit: false, list: false, visibility: 'hidden' },
DanceTitleID: { type: 'hidden', defaultValue: currentTitleID },
ChoreographerID: { title: 'Choreographer', width: '40%', options: function() { return ChoreographerOptions; } },
FormationID: { title: 'Formation', width: '30%', options: function() { return FormationOptions; } },
GenreID: { title: 'Genre', width: '30%', options: function() { return GenreOptions; } }
}
},
function(data) { data.childTable.jtable('load'); }
);
}
});
//Return image to show on the person row
return $img;
}
},
Try this
$('#BookTableContainer').find('.jtable-toolbar').remove();

Highchart y axis need 2 category each having 3 sub category

I am using high chart version v4.0.4, i have worked bar chart like mentioned below i need output with there bar for every year details given below
I am working on below high chart code
var data_first_vd = 0;
var data_second_vd = 0;
var glb_data_ary = [];
var dynamicval1 = 5.85572581;
var dynamicval2 = 0.16091656;
if((dynamicval1>1) || (dynamicval2>1)){
var data_tit = 'Value';
var data_val = '${value}';
var prdelvalaxis = 1000000;
}else{
prdelvalaxis = 1000;
data_tit = "Value";
data_val = "${value}";
}
data_first_vd=5.86;
data_second_vd =0.16;
var data_first_ud =1397.128;
var data_second_ud = 28.145;
data_first_ud_lbl = '1.40M';
data_second_ud_lbl = '28K';
data_first_vd_lbl = '5.86M';
data_second_vd_lbl = '161K';
data_first_vd_lbl_xaxis = '$5.86M';
data_second_vd_lbl_xaxis = '$161K';
var ud1 = 1397;
var ud2 = 28;
var vd1 = 6;
var vd2 = 0;
$('#id').highcharts({
credits: { enabled: false },
chart: {
type: 'bar',
height: 200,
marginLeft: 120,
marginBottom:50,
marginTop: 47,
marginRight:30,
plotBorderWidth: 0,
},
title: {
text: null
},
xAxis: {
drawHorizontalBorders: false,
labels: {
groupedOptions: [{
rotation: 270,
}],
rotation: 0,
align:'center',
x: -30,
y: 5,
formatter: function () {
if (curYear === this.value) {
return '<span style="color:#6C9CCC;">' + this.value + '</span>';
}
else if (prevYear === this.value) {
return '<span style="color: #ED7D31;">' + this.value + '</span>';
}
else if ('VALUE' === this.value) {
return '<span style="color:#942EE1;">' + this.value + '</span>';
}
else{
return '<span style="color: #E124D2;">' + this.value + '</span>';
}
},
useHTML:true
},
categories: [{
name: "UNITS",
categories: [2017, 2016]
},{
name: "VALUE",
categories: [2017, 2016]
}
],
},
yAxis: [{ // Primary yAxis
labels: {
format: data_val,
formatter: function () {
if(this.value!=0){
return '$'+valueConverter_crt(this.value*prdelvalaxis);
}else{
return this.value;
}
},
style: {
color: '#942EE1'
}
},
title: {
text: "<span style='font-size: 12px;'>"+data_tit+"</span>",
style: {
color: '#942EE1'
},
useHTML:true
}
}, { // Secondary yAxis
title: {
text: "<span style='font-size: 12px;'>Units</span>",
style: {
color: '#E124D2'
},
useHTML:true
},
labels: {
format: '{value}',
formatter: function () {
if(this.value!=0){
return cal_pro_del_xaxis(this.value);
}else{
return this.value;
}
},
style: {
color: '#E124D2'
}
},
opposite: true
}],
tooltip: { enabled: false },
exporting: { enabled: false },
legend: { enabled: false },
plotOptions: {
series: {
dataLabels: {
inside: true,
align: 'left',
enabled: true,
formatter: function () {
return this.point.name;
},
color: '#000000',
},
stacking: false,
pointWidth: 15,
groupPadding: 0.5
},
},
series: [{
yAxis: 1,
data: [{y:1397.128,name:data_first_ud_lbl,color:'#6C9CCC'},{y:28.145,name:data_second_ud_lbl,color:'#ED7D31'}],
}, {
data: [null,null,{y:5.86,name:data_first_vd_lbl_xaxis,color:'#6C9CCC'},{y:0.16,name:data_second_vd_lbl_xaxis,color:'#ED7D31'}],
}]
});
I need out put like below chart This chart i draw in paint.
Here 3 bar added in every year
Please help me to achieve this

Unable to use OpenLayer

I'm very new with web language and I have to integrate a map in a page. I'm using cshtml with Visual Studio 2015
To do this I have take a code using ol.js. The map have to be displayed, and for each value in my table I localize the city/country from IP and I display for each countries, the number of item.
There is my Index.cshtml
<script type="text/javascript">
var map;
var countriesLayer;
var citiesLayer;
function newCountriesLayer(start, end, item, result) {
return new ol.layer.Vector({
minResolution: 2500,
source: new ol.source.GeoJSON({
projection: 'EPSG:3857',
url: '#Url.Action("GetCountries")'
+ '?StartDate=' + start
+ '&EndDate=' + end
+ '&Item=' + item
+ '&Result=' + result
}),
style: function (f, r) {
return [
new ol.style.Style({
text: new ol.style.Text({ text: f.get("title"), fill: new ol.style.Fill({ color: '#673B8F' }), scale: 1.2 }),
image: new ol.style.Circle({ radius: 10, fill: new ol.style.Fill({ color: 'white' }) }),
})
];
}
});
}
function newCitiesLayer(start, end, item, result) {
return new ol.layer.Vector({
maxResolution: 2500,
source: new ol.source.GeoJSON({
projection: 'EPSG:3857',
url: '#Url.Action("GetCities")'
+ '?StartDate=' + start
+ '&EndDate=' + end
+ '&Item=' + item
+ '&Result=' + result
}),
style: function (f, r) {
return [
new ol.style.Style({
text: new ol.style.Text({ text: f.get("title"), fill: new ol.style.Fill({ color: '#673B8F' }), scale: 1.2 }),
image: new ol.style.Circle({ radius: 10, fill: new ol.style.Fill({ color: 'white' }) }),
})
];
}
});
}
$(document).ready(function () {
var start = $('#startDate').val();
var end = $('#endDate').val();
var item = $('#item').val();
var result = $('#resultat').val();
countriesLayer = newCountriesLayer(start, end, item, result);
citiesLayer = newCitiesLayer(start, end, item, result);
map = new ol.Map({
target: 'map',
renderer: 'canvas',
layers:
[
new ol.layer.Tile({
//source: new ol.source.TileWMS({
// url: 'http://maps.opengeo.org/geowebcache/service/wms',
// params: { LAYERS: 'openstreetmap', VERSION: '1.1.1' }
//})
source: new ol.source.OSM(),
}),
countriesLayer, citiesLayer
],
view: new ol.View2D({
center: ol.proj.transform([0, 0], 'EPSG:4326', 'EPSG:3857'),
zoom: 1,
})
});
});
</script>
In my Controller.cs I have 2 functions:
public ContentResult GetCities(string StartDate, string EndDate, string Item, string Result){...}
public ContentResult GetCountries(string StartDate, string EndDate, string Item, string Result){...}
both return :
return new ContentResult()
{
Content = geoJson.ToString(),
ContentEncoding = System.Text.Encoding.ASCII,
ContentType = "text/json"
};
Value for geoJson is :
{
"type": "FeatureCollection",
"crs": {
"type": "name",
"properties": {
"name": "urn:ogc:def:crs:OGC:1.3:CRS84"
}
},
"features": [
{
"type": "Feature",
"properties": {
"id":"CN-30-Guangzhou",
"title":"2",
},
"geometry": {
"type": "Point",
"coordinates": [113.25,23.1167]
}
},
{
"type": "Feature",
"properties": {
"id":"CN-23-Shanghai",
"title":"1",
},
"geometry": {
"type": "Point",
"coordinates": [121.3997,31.0456]
}
},
]}
In the project that I took this code, it works. The map contain 2 points in china : a "1" is displayed in Shanghai and a "2" in Guangzhou.
In my project I have an error :
Uncaught TypeError: Cannot read property 'a' of null
at xl (ol.js:266)
at wl (ol.js:265)
at Bl (ol.js:268)
at Al (ol.js:269)
at nr (ol.js:471)
at pr.mr.f (ol.js:470)
at td (ol.js:38)
at N (ol.js:37)
at Xq.q (ol.js:467)
As I said, I'm very new to web and I lost with that error. If try to check ol.js but it is unreadable. Maybe I'm missing a library or a package but I don't know how to know.

Resources