I'm trying to create a map using Google Fusion Tables with two layers, one of which I want to add a style to. I also have multiple columns, and I want to switch between these columns using a drop-down menu. So far I managed to do the latter, but I'm stuck trying to add a second layer.
The map I have now shows the ratio of girls:boys in schools in the 170 districts in Ghana. With the drop down menu I can switch between primary school and junior high. Now I want to add a layer with the regional borders.
In the documentation I saw this:
You can use the Maps API to add up to five Fusion Tables layers to a
map, one of which can be styled with up to five styling rules.
Which is pretty much exactly what I want, but I also keep the drop-down menu. I've recently started using Fusion Tables and was hoping someone could help me.
The ID of the layer I want to add: 1_0rcifQnnNpLV1VjTPyzDZSF3LHp-7rowzrXM78
And the code of the working map:
PS: I'm a total newbie and used this map made by the Guardian as a basis. I got rid of everything I didn't think I needed, but there might be some unnecessary stuff left in the code.
<!DOCTYPE html>
<html lang="en" itemscope itemtype="http://schema.org/Article">
<head>
<title>Gender Parity Index | Education | 2011</title>
<meta charset="utf-8" />
<style type="text/css">
body { font-family: Arial, sans-serif; }
#map_canvas { height: 600px; width:575px; }
</style>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" id="script">
var center = new google.maps.LatLng(7.972198, -0.716284);
var zoom = 7;
var legend_width = '150px';
var tableid = '12GLQaH4wvwByxBk4W7UHkJTr99vsxymCTYHmkXs';
var location_column = 'geometry';
var colors = ['#CA0020','#F4A582','#F7F7F7','#92C5DE','#0571B0'];
var columns = {
'Gender parity index at primary education': [
{
'min': 0.6,
'max': 0.8,
'color': '#CA0020'
},
{
'min': 0.8,
'max': 0.95,
'color': '#F4A582'
},
{
'min': 0.95,
'max': 1.05,
'color': '#F7F7F7'
},
{
'min': 1.05,
'max': 1.2,
'color': '#92C5DE'
}
],
'Gender parity index at junior high school education': [
{
'min': 0.6,
'max': 0.8,
'color': '#CA0020'
},
{
'min': 0.8,
'max': 0.95,
'color': '#F4A582'
},
{
'min': 0.95,
'max': 1.05,
'color': '#F7F7F7'
},
{
'min': 1.05,
'max': 1.2,
'color': '#92C5DE'
},
{
'min': 1.2,
'max': 1.6,
'color': '#0571B0'
}
]
}
var map, layer, geocoder;
function initialize() {
map = new google.maps.Map(document.getElementById('map_canvas'), {
center: center,
zoom: zoom,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var style = [ { stylers: [ { invert_lightness: true } ] },{ featureType: "road.highway", stylers: [ { hue: "#00e5ff" } ] },{ featureType: "poi.park", stylers: [ { visibility: "off" } ] },{ featureType: "landscape.natural", stylers: [ { visibility: "on" } ] },{ featureType: "water", stylers: [ { color: "#080808" } ] },{ featureType: "landscape.natural", stylers: [ { color: "#202020" } ] },{ featureType: "administrative.province", elementType: "labels", stylers: [ { visibility: "on" } ] },{ featureType: "administrative.locality", elementType: "labels", stylers: [ { visibility: "on" } ] },{ featureType: "administrative.country", elementType: "labels", stylers: [ { visibility: "off" } ] },{
featureType: 'road',
elementType: 'all',
stylers: [
{ saturation: -99 }
]
} ];
geocoder = new google.maps.Geocoder();
var styledMapType = new google.maps.StyledMapType(style, {
map: map,
name: 'Styled Map'
});
map.mapTypes.set('map-style', styledMapType);
map.setMapTypeId('map-style');
layer = new google.maps.FusionTablesLayer({
query: {
select: location_column,
from: tableid
}
});
layer.setMap(map);
init_selectmenu();
addStyle(getKey());
}
function getKey() {
for(key in columns) {
return key;
}
}
// Initialize the drop-down menu
function init_selectmenu() {
var selectmenu = document.getElementById('selector');
for(column in columns) {
var option = document.createElement('option');
option.setAttribute('value', column);
option.innerHTML = column;
selectmenu.appendChild(option);
}
}
function addStyle(column) {
var defined_styles = columns[column];
var styles = new Array();
for(defined_style in defined_styles) {
var style = defined_styles[defined_style];
styles.push({
where: generateWhere(column, style.min, style.max),
polygonOptions: {
fillColor: style.color,
fillOpacity: 0.9,
strokeOpacity: 0.50,
strokeColor: "#f3f3f3"
}
});
}
layer.set('styles', styles);
updateLegend(column);
}
// Create the where clause
function generateWhere(column_name, low, high) {
var whereClause = new Array();
whereClause.push("'");
whereClause.push(column_name);
whereClause.push("' >= ");
whereClause.push(low);
whereClause.push(" AND '");
whereClause.push(column_name);
whereClause.push("' < ");
whereClause.push(high);
return whereClause.join('');
}
// Create the legend with the corresponding colors
function updateLegend(column) {
var legendDiv = document.createElement('div');
var legend = new Legend(legendDiv, column);
legendDiv.index = 1;
map.controls[google.maps.ControlPosition.RIGHT_TOP].pop();
map.controls[google.maps.ControlPosition.RIGHT_TOP].push(legendDiv);
}
// Generate the content for the legend
function Legend(controlDiv, column) {
controlDiv.style.padding = '10px';
var controlUI = document.createElement('div');
controlUI.style.backgroundColor = 'white';
controlUI.style.borderStyle = 'solid';
controlUI.style.borderWidth = '1px';
controlUI.style.width = legend_width;
controlUI.title = 'Legend';
controlDiv.appendChild(controlUI);
var controlText = document.createElement('div');
controlText.style.fontFamily = 'Arial,sans-serif';
controlText.style.fontSize = '12px';
controlText.style.paddingLeft = '4px';
controlText.style.paddingRight = '4px';
controlText.innerHTML = legendContent(column);
controlUI.appendChild(controlText);
}
function legendContent(column) {
var defined_styles = columns[column];
// Generate the content for the legend using colors from object
var controlTextList = new Array();
controlTextList.push('<p><b>');
controlTextList.push(column);
controlTextList.push('</b></p>');
for(defined_style in defined_styles) {
var style = defined_styles[defined_style];
controlTextList.push('<div style="background-color: ');
controlTextList.push(style.color);
controlTextList.push('; height: 20px; width: 20px; margin: 3px; float: left;"></div>');
controlTextList.push(style.min);
controlTextList.push(' to ');
controlTextList.push(style.max);
controlTextList.push('<br style="clear: both;"/>');
}
controlTextList.push('<br />');
return controlTextList.join('');
}
</script>
</head>
<body onload="initialize();">
<select onchange="addStyle(this.value);" id="selector" style="font-size: 16px"></select>
<div id="map_canvas"></div>
</div>
<script>
// send the query string to the iFrame
(function() {
var interactive = jQ('#interactive iframe');
if (interactive.length > 0) {
var qs = window.location.search;
interactive[0].src = interactive[0].src + qs;
}
})();
</script>
</div>
<div id="related">
</script>
</script>
</body>
</html>
Just add the second layer at the end of your initialize() method like that:
function initialize() {
// ... all the other stuff ...
borderLayer = new google.maps.FusionTablesLayer({
query: {
select: 'geometry',
from: '1_0rcifQnnNpLV1VjTPyzDZSF3LHp-7rowzrXM78'
}
});
borderLayer.setMap(map);
}
See the working example of your code with the second layer on jsFiddle.
Related
I wish to add a custom button in highcharts export menu to copy to clipboard with chrome, but it doesn't work and I get the error "Failed to read or decode Blob for clipboard item type image/png".
In my code I am trying to convert an svg image of the chart to png and create a blob then run ClipboardItem on chrome.
https://jsfiddle.net/vmjwr0fo/
Highcharts.chart('container', {
chart: {
type: 'column'
},
title: {
text: ''
},
xAxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
},
series: [{
data: [29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4]
}],
exporting: {
buttons: {
contextButton: {
menuItems: [
{
text: 'Copy to clipboard',
onclick: async function()
{
const svg = this.getSVG();
const canvas = document.createElement('canvas');
canvas.width = 1200;
canvas.height = 900;
const ctx = canvas.getContext('2d');
const img = document.createElement('img');
img.onload = function()
{
ctx.drawImage(img, 0, 0);
};
img.setAttribute('src', 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svg))));
const data = new XMLSerializer().serializeToString(img);
const pngImageBlob = await new Blob([data], {type: "image/png"});
try
{
await navigator.clipboard.write([
new ClipboardItem({
'image/png': pngImageBlob
})
]);
}
catch (error)
{
console.error(error.message);
}
}
}
]
}
}
}
});
#container {
height: 400px;
margin: 0 auto;
margin-top: 1em;
min-width: 320px;
max-width: 800px;
}
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<script src="https://code.highcharts.com/modules/export-data.js"></script>
<div id="container"></div>
Thank you in advance for your help
I have server side pagination. I've created a simple back-end service to mimic it. See https://gitlab.com/sunnyatticsoftware/sasw-community/sasw-pagination-tester
The back-end can be run as a docker container on port 5000 (or any other) with
docker run -p 5000:8080 registry.gitlab.com/sunnyatticsoftware/sasw-community/sasw-pagination-tester:latest
This is gonna allow to query items (i.e: tickets in this case) with pagination filtering. See its swagger http://localhost:5000/swagger for more info and/or to interact with the sample.
It works in a pretty standard way.
To retrieve the first page with pagination size of 5 items per page:
GET http://localhost:5000/api/tickets?Page=1&Count=5
To retrieve the second page with pagination size of 5 items per page:
GET http://localhost:5000/api/tickets?Page=2&Count=10
etcetera.
The result would have enough information to be able to draw any table on a UI with pagination links and info about total items and total pages:
{
"items": [
{
"id": "00000001-0000-0000-0000-000000000000",
"channel": "chat",
"subject": "one"
},
{
"id": "00000002-0000-0000-0000-000000000000",
"channel": "email",
"subject": "two"
},
{
"id": "00000003-0000-0000-0000-000000000000",
"channel": "email",
"subject": "three"
},
{
"id": "00000004-0000-0000-0000-000000000000",
"channel": "phone",
"subject": "four"
},
{
"id": "00000005-0000-0000-0000-000000000000",
"channel": "chat",
"subject": "five"
}
],
"pageNumber": 1,
"pageSize": 5,
"totalPages": 7,
"totalCount": 32
}
THE PROBLEM
I want to use agGrid for the UI, with infinite scrolling, but I am struggling with the examples, as they all seem to have a previous fetch of a json that later is sliced to show results while scrolling. It would've been useful to have a sample with real integration, but in theory that shouldn't be a problem.
I cannot have the infinite scroll working. For whatever reason, it loads the first items but then the agGrid does not trigger the getRows anymore, and I don't know what am I missing.
I would like to have a simple infinite scrolling example working with the server-side pagination already in place. The following html/js won't send a second request to the server after scrolling.
The index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>JavaScript example</title>
<meta charSet="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<style media="only screen">
html, body {
height: 100%;
width: 100%;
margin: 0;
box-sizing: border-box;
-webkit-overflow-scrolling: touch;
}
html {
position: absolute;
top: 0;
left: 0;
padding: 0;
overflow: auto;
}
body {
padding: 1rem;
overflow: auto;
}
</style>
</head>
<body>
<div id="myGrid" style="height: 100%" class="ag-theme-alpine-dark">
</div>
<script>var __basePath = './';</script>
<script src="https://unpkg.com/ag-grid-enterprise#28.1.3/dist/ag-grid-enterprise.min.js">
</script>
<script src="main.js">
</script>
</body>
</html>
and the main.js
(() => {
const gridOptions = {
columnDefs: [
{ valueGetter: 'node.rowIndex', maxWidth: 100 },
{ field: 'id', minWidth: 150 },
{ field: 'channel', minWidth: 150 },
{ field: 'subject', minWidth: 400 }
],
defaultColDef: {
flex: 1,
minWidth: 80,
},
rowModelType: 'serverSide',
serverSideInfiniteScroll: true,
cacheBlockSize: 10,
maxBlocksInCache: 2,
rowBuffer: 0,
//blockLoadDebounceMillis: 1000,
debug: true,
};
document.addEventListener('DOMContentLoaded', function () {
var gridDiv = document.querySelector('#myGrid');
new agGrid.Grid(gridDiv, gridOptions);
var datasource = createDataSource();
gridOptions.api.setServerSideDatasource(datasource);
});
function createDataSource() {
return {
//rowCount: undefined,
getRows: (params) => {
console.log(`params start row:${params.startRow}, params end row: ${params.endRow}`);
var pagination = getPagination(params.startRow, params.endRow);
console.log(`asking for page ${pagination.page} and count ${pagination.count}`);
// send new request
fetch(`http://localhost:5000/api/tickets?page=${pagination.page}&count=${pagination.count}`)
.then((response) => response.json())
.then(data => {
console.log("data received");
console.log(data);
console.log(params);
params.success({
rowData: data.items,
rowCount: pagination.page * pagination.count,
});
//gridOptions.api.setRowCount(data.totalCount);
});
},
};
}
function getPagination(startRow, endRow){
let pagination = {
page: 1,
count: 10
};
if(startRow && endRow){
let size = endRow - startRow;
let page = Math.ceil(startRow / size);
pagination = {
page: page,
count: size
};
}
return pagination;
}
})();
I figured it out. The problem was in the row count itself (probably) as it needs to be set to the total existing count of items that exist in back-end, so that the grid knows that needs more.
Here's a possible solution.
For ag-grid Enterprise
(() => {
const gridOptions = {
columnDefs: [
{ valueGetter: 'node.rowIndex', maxWidth: 100 },
{ field: 'id', minWidth: 150 },
{ field: 'channel', minWidth: 150 },
{ field: 'subject', minWidth: 400 }
],
defaultColDef: {
flex: 1,
minWidth: 80,
},
rowModelType: 'serverSide',
serverSideInfiniteScroll: true,
cacheBlockSize: 10, //this is the count (page size)
maxBlocksInCache: 2,
rowBuffer: 0,
debug: false
};
document.addEventListener('DOMContentLoaded', function () {
var gridDiv = document.querySelector('#myGrid');
new agGrid.Grid(gridDiv, gridOptions);
var datasource = createDataSource();
gridOptions.api.setServerSideDatasource(datasource);
});
function createDataSource() {
return {
getRows: (params) => {
let count = params.request.endRow - params.request.startRow; // or the cacheBlockSize
let page = Math.ceil(params.request.endRow / count);
console.log(`asking for page ${page} and count ${count}`);
fetch(`http://localhost:5000/api/tickets?page=${page}&count=${count}`)
.then((response) => response.json())
.then(data => {
params.success({
rowData: data.items,
rowCount: data.totalCount,
});
});
},
};
}
})();
For ag-grid Community
A few things are different,
obviously the ag-grid library is a different one.
the row model type is infinite
the grid options api method to set datasource is called setDatasource
there is no object response within the injected getRows parameter, so the startRow and endRow are retrieved directly from the params object.
at index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>JavaScript example</title>
<meta charSet="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<style media="only screen">
html, body {
height: 100%;
width: 100%;
margin: 0;
box-sizing: border-box;
-webkit-overflow-scrolling: touch;
}
html {
position: absolute;
top: 0;
left: 0;
padding: 0;
overflow: auto;
}
body {
padding: 1rem;
overflow: auto;
}
</style>
</head>
<body>
<div id="myGrid" style="height: 100%" class="ag-theme-alpine-dark">
</div>
<script>var __basePath = './';</script>
<script src="https://unpkg.com/ag-grid-community/dist/ag-grid-community.min.js">
</script>
<script src="main.js">
</script>
</body>
</html>
at main.js
(() => {
const gridOptions = {
columnDefs: [
{ valueGetter: 'node.rowIndex', maxWidth: 100 },
{ field: 'id', minWidth: 150 },
{ field: 'channel', minWidth: 150 },
{ field: 'subject', minWidth: 400 }
],
defaultColDef: {
flex: 1,
minWidth: 80,
},
rowModelType: 'infinite',
cacheBlockSize: 10, //this is the count (page size)
maxBlocksInCache: 2,
rowBuffer: 0,
debug: false
};
document.addEventListener('DOMContentLoaded', function () {
var gridDiv = document.querySelector('#myGrid');
new agGrid.Grid(gridDiv, gridOptions);
var datasource = createDataSource();
gridOptions.api.setDatasource(datasource);
});
function createDataSource() {
return {
getRows: (params) => {
console.log(params);
let count = params.endRow - params.startRow; // or the cacheBlockSize
let page = Math.ceil(params.endRow / count);
console.log(`asking for page ${page} and count ${count}`);
fetch(`http://localhost:5000/api/tickets?page=${page}&count=${count}`) // any additional filter must be set on that url as query params
.then((response) => response.json())
.then(data => {
params.successCallback(data.items, data.totalCount);
});
},
};
}
})();
I have this part of a slide code, and I want to implement the gsap split Text in h1 (NexText), so that the result is like this: https://codepen.io/GreenSock/pen/aVJRBg
However, I don't know how to implement that in my code.
function nextSlide(pageNumber) {
const nextPage = pages[pageNumber];
const currentPage = pages[current];
const nextLeft = nextPage.querySelector(".hero .model-left");
const nextRight = nextPage.querySelector(".hero .model-right");
const currentLeft = currentPage.querySelector(".hero .model-left");
const currentRight = currentPage.querySelector(".hero .model-right");
const nextText = nextPage.querySelector("h1");
const portofolio = document.querySelector(".portofolio");
const tl = new TimelineMax({
onStart: function() {
slides.forEach(slide => {
slide.style.pointerEvents = "none";
});
},
onComplete: function() {
slides.forEach(slide => {
slide.style.pointerEvents = "all";
});
}
});
tl.fromTo(currentLeft, 0.3, { y: "-10%" }, { y: "-100%" })
.fromTo(currentRight, 0.3, { y: "10%" }, { y: "100%" }, "-=0.2")
.to(portofolio, 0.3, { backgroundImage: backgrounds[pageNumber] })
.fromTo(
currentPage,
0.3,
{ opacity: 1, pointerEvents: "all" },
{ opacity: 0, pointerEvents: "none" }
)
.fromTo(
nextPage,
0.3,
{ opacity: 0, pointerEvents: "none" },
{ opacity: 1, pointerEvents: "all" },
"-=0.6"
)
.fromTo(nextLeft, 0.6, { y: "-100%" }, { y: "-10%" }, "-=0.6")
.fromTo(nextRight, 0.4, { y: "100%" }, { y: "10%" }, "-=0.8")
.fromTo(nextText, 0.3, { opacity: 0, y: 0 }, { opacity: 1, y: 0 })
.set(nextLeft, { clearProps: "all" })
.set(nextRight, { clearProps: "all" });
current = pageNumber;
}
In particular, I don't know how to make these two parts combine:
const nextText = nextPage.querySelector ("h1");
and
var mySplitText = new SplitText ("# quote", {type: "chars, words, lines"})
What you are trying to achieve can be done via:
var variableThatStoresMySplit H1= new SplitText (nextText , {type: "chars"})
And then later in your code you have to use a stagger to through the array SplitText created for you by doing:
gsap.staggerFrom(variableThatStoresMySplit .chars, 0.5, {y:20, opacity:0}, 0.04)
I hope this solves your issue
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.
I need x-axis labels in different colors, I am using "chart.js". I tried below code but it is not working, just showing single color-
scales: {
xAxes: [{
ticks: {
fontColor: [
'rgba(245,88,97,1)',
'rgba(245,88,97,1)',
'rgba(245,88,97,1)',
'rgba(145,151,163,1)',
'rgba(70,180,220,1)',
'rgba(70,180,220,1)',
'rgba(70,180,220,1)'
]
}
}]
}
Output:
Need:
You can make use of the Plugin Core API. It offers different hooks that may be used for executing custom code. In below code snippet, I use the afterDraw hook to draw text of the same color as the corresponding bar.
chart.data.labels.forEach((l, i) => {
var value = chart.data.datasets[0].data[i];
var x = xAxis.getPixelForValue(l);
ctx.fillStyle = chart.data.datasets[0].backgroundColor[i];
ctx.fillText(l, x, yAxis.bottom + 17);
});
When drawing your own tick labels, you need to instruct Chart.js not to display the default labels. This can be done through the following definition inside the chart options.
scales: {
xAxes: [{
ticks: {
display: false
}
}],
You also need to define some padding for the bottom of the chart, otherwise you won't see your custom tick labels.
layout: {
padding: {
bottom: 20
}
},
Please take a look at the following sample code that illustrates how to change the labels on the x-axis depending on the values.
new Chart('myChart', {
type: 'bar',
plugins: [{
afterDraw: chart => {
var ctx = chart.chart.ctx;
var xAxis = chart.scales['x-axis-0'];
var yAxis = chart.scales['y-axis-0'];
ctx.save();
ctx.textAlign = 'center';
ctx.font = '12px Arial';
chart.data.labels.forEach((l, i) => {
var value = chart.data.datasets[0].data[i];
var x = xAxis.getPixelForValue(l);
ctx.fillStyle = chart.data.datasets[0].backgroundColor[i];
ctx.fillText(l, x, yAxis.bottom + 17);
});
ctx.restore();
}
}],
data: {
labels: ["-3", "-2", "-1", "0", "+1", "+2", "+3"],
datasets: [{
label: "My First Dataset",
data: [60, 59, 80, 81, 60, 55, 40],
fill: false,
backgroundColor: ['rgba(245,88,97,1)', 'rgba(245,88,97,1)', 'rgba(245,88,97,1)', 'rgba(145,151,163,1)', 'rgba(70,180,220,1)', 'rgba(70,180,220,1)', 'rgba(70,180,220,1)'],
borderWidth: 1
}]
},
options: {
layout: {
padding: {
bottom: 20
}
},
scales: {
xAxes: [{
ticks: {
display: false
}
}],
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
canvas {
max-width: 300px
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<canvas id="myChart" height="200"></canvas>