I'll need to use correct component to implement this https://imgur.com/a/P2VHX2i . I'm currently looking at TreeList Examples ( https://demos.telerik.com/kendo-ui/treelist/remote-data-binding ) using json data from server. Can you give some advice on creating this treelist?
I did a PoC once for a similar scenario like yours. I've simulated a TreeList with a Grid, and it worked:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Untitled</title>
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2019.3.917/styles/kendo.common.min.css">
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2019.3.917/styles/kendo.rtl.min.css">
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2019.3.917/styles/kendo.default.min.css">
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2019.3.917/styles/kendo.mobile.all.min.css">
<style type="text/css">
.item-level {
display: inline-block;
width: 10px;
}
.level-arrow-expanded:before {
content: "\00bb"
}
.level-arrow-collapsed:before {
content: "\00ab"
}
.item-cell {
cursor: pointer
}
td[role="gridcell"] {
padding: 0;
}
.cell-container {
display: inline-block;
padding-bottom: 6.4px;
padding-left: 9.6px;
padding-right: 9.6px;
padding-top: 6.4px;
box-sizing: border-box;
width: 100%;
}
.header-container {
padding: 0
}
.header-editable-cell {
color: #9cc3e5;
font-weight: bold
}
.k-grid tr:hover td .cell-container {
background-color: #bdb4af !important
}
</style>
<script src="https://code.jquery.com/jquery-1.12.3.min.js"></script>
<script src="https://kendo.cdn.telerik.com/2019.3.917/js/angular.min.js"></script>
<script src="https://kendo.cdn.telerik.com/2019.3.917/js/jszip.min.js"></script>
<script src="https://kendo.cdn.telerik.com/2019.3.917/js/kendo.all.min.js"></script>
<script id="line-template" type="text/x-kendo-template">
<div class='cell-container'>
<div class='item-level #= (data.Level < 4 ? "level-arrow-" + (data.Collapsed ? "collapsed" : "expanded") : "") #'></div>
# for (let i = -1; i < (data.Level - 1); i++) { #
<div class='item-level level-space'></div>
# } #
#= data.Line #
</div>
</script>
<script>
$(function() {
const lineTemplate = $("#line-template").html();
let data = [];
let cols = [{
title: " ",
field: "Line",
locked: true,
width: 200,
template: lineTemplate,
attributes: { "class": "item-cell" }
}, {
title: "Customer Type",
field: "CustomerType",
width: 300
}];
for (var n = 0; n < 50000; n++) {
let level = (n % 5),
dataItem = {
Line: n,
CustomerType: (n % 2 == 0 ? "All" : "3rd Party"),
Level: level,
Show: true,
Index: n,
Collapsed: false
};
data.push(dataItem);
}
let grid = $("#grid").kendoGrid({
dataSource: {
data: data,
pageSize: 20,
filter: { field: "Show", operator: "eq", value: true }
},
height: 500,
columns: cols,
scrollable: {
virtual: true
}
}).data("kendoGrid");
grid.lockedTable.on("click", "td.item-cell", function() {
let data = grid.dataSource.data(),
dataItem = grid.dataItem($(this).closest("tr")),
item;
for (let i = 0, count = data.length; i < count; i++) {
item = data[i];
if (item.Index == dataItem.Index) {
dataItem.Collapsed = !dataItem.Collapsed;
}
else if (item.Index > dataItem.Index) {
if (dataItem.Index != i && item.Level == 0) {
break;
}
item.Show = !dataItem.Collapsed;
}
}
grid.dataSource.fetch();
});
});
</script>
</head>
<body>
<div id="grid"></div>
</body>
</html>
Demo in Dojo
Why I did that? To work with huge datasets which I don't know if is your case tho. As you can see, the above TreeList is displaying 50k rows, virtualized. The problem is that TreeList does not virtualizes(check out it's docs), but Grid does.
How that works:
Lock the first column to be the collpase/expand column. You can also lock more columns;
In your dataItems(row's data), you have to use some control properties, which are: Collapsed, Level, Show and Index. They control the collpase/expand behaviour and the hierarchical display. Add a console.log(data) before Grid's initialization to checkout how it's been filled;
The Show property handles the collpase/expand displaying status. Once a row is Show = false, it will be hidden. That works because of a filter in the dataSource: filter: { field: "Show", operator: "eq", value: true };
A click event on the item-cell class manages the row's children display status. That class is added in the first column as: attributes: { "class": "item-cell" }. The event changes the Show property for the children rows;
The template line-template will display the right level indetation and the arrows for the collpase/expand column;
Related
I learned cytoscape.js and related extension some days and tried its many amazing features.
I find that when the number of children is greater than 5 and expand a node, the whole graph fly out of screen.
I constructed more complex data which own 3 parent nodes and 5 child nodes per parent node. There is a connection between any two child nodes.
So there are 3 parent nodes, 15 children nodes and 14+13+12..1 links.
To sum up, when there are more links, the layout behavior looks abnormal.
See my demo below.
You can modify parameters of my function getInitData() to see the effect.
document.addEventListener('DOMContentLoaded', function(){
var cy = window.cy = cytoscape({
container: document.getElementById('cy'),
ready: function(){
var api = this.expandCollapse({
layoutBy: {
name: "cose-bilkent",
animate: 'end',
randomize: false,
fit: false,
idealEdgeLength : 150
},
fisheye: false,
animate: true,
undoable: false
});
api.collapseAll();
},
style: [
{
selector: 'node',
style: {
'background-color': '#ad1a66'
}
},
{
selector: ':parent',
style: {
'background-opacity': 0.333
}
},
{
selector: "node.cy-expand-collapse-collapsed-node",
style: {
"background-color": "darkblue",
"shape": "rectangle"
}
},
{
selector: 'edge',
style: {
'width': 3,
'line-color': '#ad1a66'
}
},
{
selector: 'edge.meta',
style: {
'width': 2,
'line-color': 'red'
}
},
{
selector: ':selected',
style: {
"border-width": 3,
"border-color": '#DAA520'
}
}
],
elements : getInitData(3, 5)
});
var api = cy.expandCollapse('get');
var elements = null;
});
function getInitData(parentNum, childrenNum){
var data = [], children = [], i, j, n;
for(i = 0; i < parentNum; i++){
n = "parent"+i;
data.push({"group":'nodes',data:{"id":n}});
for(j = 0; j < childrenNum; j++){
children.push({"group":'nodes',data:{"id":n+"_child_"+j, parent:n}});
}
}
var s,t;
for(i = 0; i < children.length - 1; i++){
s = children[i].data.id;
for(j = i+1; j < children.length; j++){
t = children[j].data.id;
data.push({"group":'edges',data:{"id":s+"_"+t, source:s, target:t}});
}
}
return data.concat(children);
}
body {
font-family: helvetica neue, helvetica, liberation sans, arial, sans-serif;
font-size: 14px;
}
#cy {
z-index: 999;
width: 100%;
height: 100%;
}
h1 {
opacity: 0.5;
font-size: 1em;
font-weight: bold;
}
<script src="https://code.jquery.com/jquery-2.0.3.min.js"></script>
<script src="https://unpkg.com/cytoscape#3.1.0/dist/cytoscape.min.js"></script>
<!-- for testing with local version of cytoscape.js -->
<!--<script src="../cytoscape.js/build/cytoscape.js"></script>-->
<script src="https://unpkg.com/cytoscape-cose-bilkent#4.0.0/cytoscape-cose-bilkent.js"></script>
<script src="https://unpkg.com/cytoscape-expand-collapse#3.1.1/cytoscape-expand-collapse.js"></script>
<div id="cy"></div>
Solution one:
Your graph doesn't have any fitting logic. You can implement that yourself with the two methods cy.center(), which centers the graph to the current viewport and cy.fit(), which zooms the graph to the right position. You would have to call these mehtods everytime you change your graph, e.g. when you add a node, remove a node or, like in your case, expand and collapse. You can do that by binding these events and calling the said methods there.
Binding, as you know from the last question works like this:
cy.unbind('event');
cy.bind('event', 'target', function (event) {...});
Solution two:
You can alternatively, if possible (not all layouts can do this), set the method to fit: true,, which fits the graph with cy.fit(); and cy.center(); internally.
Additional problem and solution for that:
You said, that your graph looks bad when you only have one node in it, so to circumvent that, you can set the padding property of 'cose-bilkent' to a higher number. You can do that at the initialization in the options.
document.addEventListener('DOMContentLoaded', function() {
var padding = 10;
var cy = window.cy = cytoscape({
container: document.getElementById('cy'),
layout: {
name: 'cose-bilkent',
animate: false,
randomize: true
},
style: [{
selector: 'node',
style: {
'background-color': '#ad1a66'
}
},
{
selector: 'edge',
style: {
'width': 3,
'line-color': '#ad1a66'
}
}
],
elements: [{
"data": {
"id": "glyph9"
}
}]
});
document.getElementById("add").addEventListener("click", function() {
padding += 10;
var layout = cy.layout({
name: 'cose-bilkent',
animate: false,
padding: padding
});
layout.run();
});
});
body {
font: 14px helvetica neue, helvetica, arial, sans-serif;
}
#cy {
height: 90%;
width: 100%;
position: absolute;
float: left;
}
button {
margin-right: 10px;
}
<!DOCTYPE>
<html>
<head>
<title>cytoscape-cose-bilkent.js demo</title>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
<script src="https://code.jquery.com/jquery-2.0.3.min.js"></script>
<script src="https://unpkg.com/cytoscape#3.1.0/dist/cytoscape.min.js"></script>
<!-- for testing with local version of cytoscape.js -->
<!--<script src="../cytoscape.js/build/cytoscape.js"></script>-->
<script src="https://unpkg.com/cytoscape-cose-bilkent#4.0.0/cytoscape-cose-bilkent.js"></script>
<script src="https://unpkg.com/cytoscape-expand-collapse#3.1.1/cytoscape-expand-collapse.js"></script>
</head>
<body>
<button id="add" type="button">Add padding</button>
<div id="cy"></div>
</body>
</html>
As I posted a previous question, I am using Highcharts to consume a REST API (Spring app) (http://85614a50.ngrok.io/api/devices) with 2 series (axes)
The first series is memory vs time, so the object contains (Memory usage, timestamp).
The second series is CPU vs time, so the object contains (CPU Usage, timestamp).
This data is static right now, but I want it to be dynamic.
I created a function which does a call to the REST API every 5 seconds and an event function inside the Chart refreshing every 5 seconds.
I tried to declare variables inside the event function with series, but it gives me this error:
Uncaught TypeError: Cannot read property 'addPoint' of undefined
Code Pen example: https://codepen.io/anon/pen/eLEyGb
Home.blade.php
#extends('index.app')
#section('main')
<style type="text/css">
#container{
width: 100%;
margin: 0 auto;
}
.col-lg-4 {
margin-bottom: 10%;
min-width: 40%;
max-width: 100%;
margin: 1em auto;
height: 400px;
}
.modal {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 1; /* Sit on top */
padding-top: 10%; /* Location of the box */
padding-right: 10%;
padding-left: 10%;
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}
#container.modal{
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
background: rgba(0, 0, 0, 0.5);
display:block;
}
</style>
<h2 class="text-center" >{{$user->first_name}} Charts </h2>
<div id="container">
<div class="row"></div>
</div>
<script type="text/javascript">
$(function () {
setInterval(getHighChart, 10000); //30 seconds onload="getHighChart()"
});
function getHighChart() {
$.getJSON( "http://localhost:8000/api/devices", function( data ) {
console.log(data);
var mappedClientsAllias = _.map(_.uniqBy(data, "clientName"), "clientAllias");
var mappedClients = _.map(_.uniqBy(data, "Id_client"), "Id_client");
var devices= [];
_.forEach(mappedClients, function(Id_client, clientName) {
var tempClient = {
Allias: mappedClientsAllias[clientName],
name: Id_client,
data: [],
memory:[]
};
tempClient2=tempClient;
_.forEach(data, function(tempData) {
if (clientId === tempData.clientId) {
_.forEach(tempData.clientData, function(clientData) {
tempClient.data.push([
clientData.timestamp,
clientData.cpuUsage,
]);
tempClient.memory.push([
clientData.timestamp,
clientData.memoryUsage,
]);
});
}
});
devices.push(tempClient);
});
console.log("devices", devices);
var chart = _.forEach(devices, function(device) {
$('<div class="col-lg-4">')
.css("position", "relative")
.appendTo("#container")
.highcharts("StockChart", {
marker: {
states: {
enabled: true
}
},
time: {
timezoneOffset: -5 * 60
},
exporting: {
buttons: {
customButton3: {
text: 'Zooming',
//make fullscreen of chart with size change
onclick: function(b) {
var w = $(window).width();
var h = $(window).height();
$(b.target).closest('#container').toggleClass('modal');
if($(b.target).closest('#container').hasClass('modal')) {
$('.col-lg-4').hide();
$(b.target).closest('.col-lg-4').show();
$('.col-lg-4').css({
'width': w * .9,
'height': h * .9
});
} else {
$('.col-lg-4').show();
$('.col-lg-4').css({
'width': '',
'height': ''
});
}
$(b.target).closest('.col-lg-4').highcharts().reflow();
}
}
}
},
rangeSelector: {
y: 15,
buttons: [
{
count: 1,
type: "minute",
text: "Sec"
},
{
count: 1,
type: "hour",
text: "Min"
},
{
count: 1,
type: "day",
text: "Hours"
},
{
type: "all",
text: "All"
}
],
title: "hours",
inputEnabled: true,
_selected: 1
},
title: {
text: device.Allias
},
yAxis: [{
labels: {
enabled: true,
align: 'right',
x: -3
},
title: {
text: 'CPU'
},
height: '50%',
lineWidth: 2,
color: 'red'
}, {
labels: {
align: 'right',
x: -3
},
title: {
text: 'Memory'
},
top: '70%',
height: '50%',
offset: 0,
lineWidth: 2,
}],
xAxis: {
tickInterval: 1,
title: {
enabled: true,
text: "Client usage"
},
top: '20%',
type: "datetime",
dateTimeLabelFormats: {
second: "%H:%M:%S",
minute: "%H:%M",
hour: "%H:%M",
day: "%e. %b",
week: "%e. %b",
day: "%Y.%b-%d"
}
},
plotOptions: {
series: {
marker: {
enabled: false,
}
}
},
series: [{
name: "Memory USAGE",
data: device.memory.sort()
}, // Add a new series
{
name: "Cpu USAGE",
yAxis: 1,
color: 'red',
data: device.data.sort()
}],
chart: {
renderTo: "container",
height:400,
events: {
load: function () {
// set up the updating of the chart each second
var series = this.series[0];
setInterval(function () {
var x = (new Date()).getTime(), // current time
y = Math.round(Math.random() * 100);
series.addPoint([x, y], false, true);
}, 1000);
}
}
},
});
});
});
}
</script>
#endsection
Layouts.app
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>Master thesis application</title>
<!-- Jquery -->
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="//code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>
<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<link rel="stylesheet" type="text/css" href="https://getbootstrap.com/docs/3.3/examples/jumbotron-narrow/">
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css">
<!-- Import css file-->
<link href="{{asset('css/app.css')}}" rel="stylesheet" type="text/css"/>
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="https://code.highcharts.com/stock/highstock.js"></script>
<script src="https://code.highcharts.com/stock/modules/exporting.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>
</head>
<body>
#include('file.hed')
#include('file.bar')
<script>
window.onscroll = function() {myFunction()};
var navbar = document.getElementById("navbar");
var sticky = navbar.offsetTop;
function myFunction() {
if (window.pageYOffset >= sticky) {
navbar.classList.add("sticky")
} else {
navbar.classList.remove("sticky");
}
}
</script>
<div class="container">
#include('file.info')
#yield('main')
</div> <!-- /container -->
#include('file.down')
</body>
</html>
You've got the error because of this part of code:
events: {
load: function (series) {
var memory=client.memory.sort();
setInterval(function () {
var x = memory;
series[0].addPoint([x], true, true);
}, 5000);
}
}
There you are passing the series argument to event function, which actually isn't the series. It's just the event. If you want to refer to the series array, please do it like below:
events: {
load: function () {
var memory = client.memory.sort(),
series = this.series;
setInterval(function () {
var x = memory;
series[0].addPoint([x], true, true);
}, 5000);
}
}
BTW. I don't quite understand why you are adding the new chart every 5 seconds, instead of updating old ones.
Live example: https://codepen.io/anon/pen/oPGYoZ
I am using SlickGrid in my project and when i try to reorder columns , web page scrolls vertically and column headers become invisible .I found that in SlickGrid.js , in function setupColumnReorder, start function is called when user begins to drag column. I changed the start and stop functions in setupColumnReorder as below. This time , first time when user begins to drag column header , web page scrolls automatically. When the user drags second time it works as intended and no vertical scrolling occurs . How to prevent vertical scrolling in the first drag operation ?
function setupColumnReorder() {
$headers.filter(":ui-sortable").sortable("destroy");
$headers.sortable({
containment: "parent",
distance: 3,
axis: "x",
cursor: "default",
tolerance: "intersection",
helper: "clone",
placeholder: "slick-sortable-placeholder ui-state-default slick-header-column",
start: function (e, ui) {
/********************I added this part******************************************************/
$("body").css("overflow", "hidden");
$(this.parentNode).css("overflow", "hidden");
// window.scrollTo(0, 0) ;
/********************I added this part******************************************************/
ui.placeholder.width(ui.helper.outerWidth() - headerColumnWidthDiff);
$(ui.helper).addClass("slick-header-column-active");
},
beforeStop: function (e, ui) {
$(ui.helper).removeClass("slick-header-column-active");
},
stop: function (e) {
/********************I added this part******************************************************/
$("body").css("overflow", "auto");
$(this.parentNode).css("overflow", "auto");
/********************I added this part******************************************************/
Edit : I am adding an example that vertical scrolling occurs .
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="shortcut icon" type="image/ico" href="favicon.ico" />
<title>SlickGrid example 2: Formatters</title>
<link rel="stylesheet" href="../slick.grid.css" type="text/css"/>
<link rel="stylesheet" href="../css/smoothness/jquery-ui-1.11.3.custom.css" type="text/css"/>
<link rel="stylesheet" href="examples.css" type="text/css"/>
<style>
.cell-title {
font-weight: bold;
}
.cell-effort-driven {
text-align: center;
}
.green {
background-color: green;
}
.red {
background-color: red;
}
.orange {
background-color: orange;
}
</style>
<script src="../lib/firebugx.js"></script>
<script src="../lib/jquery-1.11.2.min.js"></script>
<script src="../lib/jquery-ui-1.11.3.min.js"></script>
<script src="../lib/jquery.event.drag-2.3.0.js"></script>
<script src="../slick.core.js"></script>
<script src="../slick.formatters.js"></script>
<script src="../slick.grid.js"></script>
<script>
// a standard formatter returns a string
function formatter(row, cell, value, columnDef, dataContext) {
return value;
}
// an extended formatter returns an object { text , removeClasses, addClasses }
// the classes are removed and then added during an update, or just added on cell creation
function statusFormatter(row, cell, value, columnDef, dataContext) {
var rtn = { text: value, removeClasses: 'red orange green' };
if (value !== null || value !== "") {
if (value < 33) {
rtn.addClasses = "red";
} else if (value < 66) {
rtn.addClasses = "orange";
} else {
rtn.addClasses = "green";
}
}
return rtn;
}
var grid;
var data = [];
var columns = [
{id: "title", name: "Title", field: "title", width: 120, cssClass: "cell-title", formatter: formatter},
{id: "duration", name: "Duration", field: "duration"},
{id: "%", name: "% Complete", field: "percentComplete", width: 80, resizable: false, formatter: Slick.Formatters.PercentCompleteBar},
{id: "status", name: "Status", field: "percentComplete", width: 50, resizable: false, formatter: statusFormatter},
{id: "start", name: "Start", field: "start", minWidth: 60},
{id: "finish", name: "Finish", field: "finish", minWidth: 60},
{id: "effort-driven", name: "Effort Driven", sortable: false, width: 80, minWidth: 20, maxWidth: 80, cssClass: "cell-effort-driven", field: "effortDriven", formatter: Slick.Formatters.Checkmark}
];
var options = {
editable: false,
enableAddRow: false,
enableCellNavigation: true
};
$(function () {
for (var i = 0; i < 5; i++) {
var d = (data[i] = {});
d["title"] = "<a href='#' tabindex='0'>Task</a> " + i;
d["duration"] = "5 days";
d["percentComplete"] = Math.min(100, Math.round(Math.random() * 110));
d["start"] = "01/01/2009";
d["finish"] = "01/05/2009";
d["effortDriven"] = (i % 5 == 0);
}
grid = new Slick.Grid("#myGrid", data, columns, options);
})
</script>
</head>
<body>
<table width="100%">
<tr>
<td valign="top" width="50%">
<div id="myGrid" style="position:absolute;top:300px;width:700px;height:500px;"></div>
</td>
<td valign="top">
<h2>Demonstrates:</h2>
<ul>
<li>width, minWidth, maxWidth, resizable, cssClass column attributes</li>
<li>custom column formatters</li>
<li>an extended formatter returning an object { text , removeClasses, addClasses } rather than a string, allowing adding and removing css classes from the cell</li>
</ul>
<h2>View Source:</h2>
<ul>
<li> View the source for this example on Github</li>
</ul>
</td>
</tr>
</table>
</body>
</html>
Edit : The problem seems to be related to Mozilla Firefox. I tested the above code with Google Chrome and no vertical scrolling occurs .
Have you replicated the bug in the basic examples?
You can drag columns in http://6pac.github.io/SlickGrid/examples/example2-formatters.html
Does the problem occur there?
I'm not getting any issues, so if you are please specify the browser and OS you are using.
I'm trying to make an Excel-like editor with HandsOnTable but I haven't yet figured out how to change a cell's style dynamically, borders in this case.
I have tried to use
setCellMeta(row,col,"borders", My_borders_Object);
and then
MyHotInstance.render();
but this had no effect.
What could I do to solve this problem?
Any help will be very appreciated.
Not sure what my_borders_object is or why you're passing "borders" as a cell meta data argument, but here's a good way of doing it:
There is an initialization option called customBorders; see below for excerpt from documentation:
customBorders : Boolean (default false)
customBorders : Array [{ row: 2, col: 2, left: {width:2, color: 'red'}, right: {width:1, color: 'green'}, top: /*...*/, bottom: /*...*/ }]
customBorders : Array [{ range:{ from:{ row: 1, col: 1 }, to:{ row: 3, col: 4 } }, left: { /*...*/ }, right: { /*...*/ }, top: { /*...*/ }, bottom: { /*...*/ } }]
If true, enables Custom Borders plugin, which enables applying custom borders through the context menu (configurable with context menu key borders).
To initialize Handsontable with predefined custom borders, provide cell coordinates and border styles in form of an array.
See Custom Borders demo for examples.
Version added: 0.11.0
What this means is that at any given point, if you wanted to do a dynamic update of borders, you can use
hotInstance.updateSettings({
customBorders: new_borders_array
})
I'm trying to accomplish the same right now actually. I have tried the following:
ht is the handsontable instance
ht.updateSettings({
customBorders: [
{ range:
{
from: { row: 1, col: 15 },
to: { row: 1, col: 16 }
},
top: { width: 3, color: 'red' },
left: { width: 2, color: 'red' },
bottom: { width: 2, color: 'red' },
right: { width: 2, color: 'red' }
},
]
});
Without ht.init() it does not work:
ht.init();
In version 0.17 this worked fine, however after the update in version 0.18 ht.init(); it creates another instance of the table below the current one - very frustrating.
So now I'm again stuck, or I will downgrade to 0.17 until this is fixed in 0.18.
After going thought the handsontable.full.js I managed to do it by extracting some function from the code and building the borders objects:
var container = document.getElementById('ht_container');
var data = function () {
return Handsontable.helper.createSpreadsheetData(20, 12);
};
var hot = new Handsontable(container, {
data: data(),
height: 396,
colHeaders: true,
rowHeaders: true,
stretchH: 'all',
customBorders: true,
});
//get handsontable instance
var instance = hot;
//copy required functions from the JS.... not pretty, but easy enough
//instead of building the required objects manually
var getSettingIndex = function(className) {
for (var i = 0; i < instance.view.wt.selections.length; i++) {
if (instance.view.wt.selections[i].settings.className == className) {
return i;
}
}
return -1;
};
var insertBorderIntoSettings = function(border) {
var coordinates = {
row: border.row,
col: border.col
};
var selection = new WalkontableSelection(border, new WalkontableCellRange(coordinates, coordinates, coordinates));
var index = getSettingIndex(border.className);
if (index >= 0) {
instance.view.wt.selections[index] = selection;
} else {
instance.view.wt.selections.push(selection);
}
};
var createClassName = function(row, col) {
return "border_row" + row + "col" + col;
};
var createDefaultCustomBorder = function() {
return {
width: 1,
color: '#000'
};
};
var createSingleEmptyBorder = function() {
return {hide: true};
};
var createDefaultHtBorder = function() {
return {
width: 1,
color: '#000',
cornerVisible: false
};
};
var createEmptyBorders = function(row, col) {
return {
className: createClassName(row, col),
border: createDefaultHtBorder(),
row: row,
col: col,
top: createSingleEmptyBorder(),
right: createSingleEmptyBorder(),
bottom: createSingleEmptyBorder(),
left: createSingleEmptyBorder()
};
};
var prepareBorderFromCustomAddedRange = function(rowObj) {
var range = rowObj.range;
for (var row = range.from.row; row <= range.to.row; row++) {
for (var col = range.from.col; col <= range.to.col; col++) {
var border = createEmptyBorders(row, col);
var add = 0;
if (row == range.from.row) {
add++;
if (rowObj.hasOwnProperty('top')) {
border.top = rowObj.top;
}
}
if (row == range.to.row) {
add++;
if (rowObj.hasOwnProperty('bottom')) {
border.bottom = rowObj.bottom;
}
}
if (col == range.from.col) {
add++;
if (rowObj.hasOwnProperty('left')) {
border.left = rowObj.left;
}
}
if (col == range.to.col) {
add++;
if (rowObj.hasOwnProperty('right')) {
border.right = rowObj.right;
}
}
if (add > 0) {
this.setCellMeta(row, col, 'borders', border);
insertBorderIntoSettings(border);
}
}
}
};
$(document).ready(function () {
//create my borders object
var customBorders = [
{ range:
{
from: { row: 1, col: 2 },
to: { row: 4, col: 4 }
},
top: { width: 3, color: 'red' },
left: { width: 2, color: 'red' },
bottom: { width: 2, color: 'red' },
right: { width: 2, color: 'red' }
},
];
//used the 'stolen' functions to add them to the HT in
prepareBorderFromCustomAddedRange.call(instance, customBorders[0]);
instance.render();
instance.view.wt.draw(true);
instance.customBorders = customBorders;
});
</style>
<script src="https://code.jquery.com/jquery-1.11.1.min.js"></script>
<link href="http://handsontable.com//styles/main.css" rel="stylesheet">
<link href="http://handsontable.com//bower_components/handsontable/dist/handsontable.full.min.css" rel="stylesheet">
<script src="http://handsontable.com//bower_components/handsontable/dist/handsontable.full.min.js"></script>
<style type="text/css">
body {background: white; margin: 20px;}
h2 {margin: 20px 0;}
<div id="ht_container"></div>
But if you are not lazy, you can build pretty much build your 'border' object and use, insertBorderIntoSettings to add them to your table or write custom code that does the same.
I'm trying to set a filter on a dojo grid programatically so that some column be greater than some number.
I have a quantity column and I wish to set a filter so that quantity be greater (or different) than zero.
Thanks!
So the code would look like this:
dojo.require("dojox.grid.EnhancedGrid");
var storage = new dojo.data.ItemFileWriteStore({data: data});
var layout = [ { field: 'id', name: '# ID', width: '60px' },
{ field: 'name', name: 'Nume produs', width: '200px' },
{ field: 'qty', name: 'QTY', alwaysEditing: true, editable: true }
];
var grid = new dojox.grid.EnhancedGrid({
store: storage,
structure: layout,
plugins: { filter : true },
}, 'grid');
grid.startup();
I'm guessing I should do a filter or a query but I really don't know how to compare two things, the only thing I found out is that I can use a query with RegEx but.. that's all
Doing
var grid = new dojox.grid.EnhancedGrid({
query: { 'qty' : new RegExp('^[1-9]+[0-9]*$') },
store: storage,
structure: layout,
plugins: { filter : true },
}, 'grid');
works but I believe it's rather inefficient and I hoped for a nicer, cleaner, faster solution.
Here's how I ended up doing something similar:
The basic idea is to add an invisible column. When the value you want to test meets your criteria (greater than some value, or in my case within some range) then set the invisible column to 'Y', otherwise set it to 'N'. Then the filter is easy: invisibleColumn: 'Y'.
The following displays a grid with Column_B values between 100-500. Click the button to activate a filter to display only values between 150-350. It is a complete working example in three parts (javascript, HTML, CSS):
The JavaScript (script.js)
dojo.require("dijit.layout.ContentPane");
dojo.require("dijit.form.Button");
dojo.require("dojox.grid.DataGrid");
dojo.require("dojo.data.ItemFileReadStore");
dojo.ready(function(){
var appLayout = new dijit.layout.ContentPane({
id: "appLayout"
}, "appLayout");
var data = {
'items': [
{'Column_A': 'alpha', 'Column_B': 100},
{'Column_A': 'beta', 'Column_B': 200},
{'Column_A': 'gamma', 'Column_B': 300},
{'Column_A': 'delta', 'Column_B': 400},
{'Column_A': 'epsilon', 'Column_B': 500}
]
};
var store = new dojo.data.ItemFileReadStore({
data: data
});
var layout = [[
{name : 'A', field : 'Column_A', width : '125px'},
{name : 'B', field : 'Column_B', width : '125px%'}
]];
var grid = new dojox.grid.DataGrid({
structure : layout,
store: store,
queryOptions: {ignoreCase: true}
});
var filterButton = new dijit.form.Button({
label: "Filter",
onClick: function () {
var determineRange = function (items, request) {
for (var i = 0; i < items.length; ++i) {
items[i].invisibleColumn = (items[i].Column_B > 150 && items[i].Column_B < 350) ? 'Y' : 'N';
}
grid.filter({invisibleColumn: 'Y'});
};
store.fetch({onComplete: determineRange});
}
});
filterButton.placeAt(appLayout.domNode);
grid.placeAt(appLayout.domNode);
appLayout.startup();
});
The html (index.html)
<html lang="en">
<head>
<meta charset="utf-8">
<title>Filter by range</title>
<link rel="stylesheet" href="style.css" media="screen"/>
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dijit/themes/claro/claro.css" />
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dijit/themes/claro/document.css" />
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/layout/resources/ExpandoPane.css" />
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/grid/resources/claroGrid.css">
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/grid/enhanced/resources/claro/EnhancedGrid.css">
<script src="http://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojo/dojo.js" type="text/javascript"></script>
<script src="script.js" type="text/javascript"></script>
</head>
<body class="claro">
<div id="appLayout"></div>
</body>
</html>
And finally the CSS (style.css)
html, body {
width: 100%; height: 100%;
margin: 0; padding: 0;
overflow: hidden;
}
#appLayout {
width 100%; height: 100%;
}