Handsontable and hot-formula-parser - handsontable

is it possible to include/register a custom function ('ADD_5') into handsontable?
The following example is working and all cell-values from B2 to B4 are equal to 42. Only B5 = ADD_5(37) returns #NAME? instead of 42.
Where Do I have to define the function and add it to the existing parser of the table?
<div id="example1" class="handsontable"></div>
<script data-jsfiddle="example1">
$(document).ready(function () {
var data1 = [
['Number', 'Answer'],
[10, 42],
[10, '=SUM(A2:A5)'],
[10, '=A2 + A3 + A4 + A5'],
[12, '=ADD_5(37)']
];
var container1 = $('#example1');
container1.handsontable({
data: data1,
minSpareRows: 1,
colHeaders: true,
rowHeaders: true,
contextMenu: true,
manualColumnResize: true,
formulas: true
});
parser.setFunction('ADD_5', function(params) {
return params[0] + 5;
});
});
</script>
Any help would be appreciated.

According to official doc:
The Formulas plugin allows Handsontable to process formula expressions defined in the provided data. This plugin uses a formula-parser library which takes most of functions from formula.js.
You would need to add your formula to formula.js then

Related

DataTables: Custom Response Handling

I started working on AngularJS and DataTables and wonder whether it is possible to customize the response DataTables is expecting. The current expectation of the DataTables plugin is something like this:
{
"draw": 1,
"recordsTotal": 57,
"recordsFiltered": 5,
"data": [...]
}
On the server end, the API's are being handled by django-tastypie
The response from server is:
{
meta: {
limit: 20,
next: null,
offset: 0,
previous: null,
total_count: 2
},
objects: [...]
}
So, is there a way to tweak Datatables Plugin to accept/map this response, or I'll have to find a way to add expected fields to the api?
So far I've done this:
var deptTable = angular.element('#deptManagementTable').DataTable({
processing: true,
serverSide: true,
pagingType: "simple_numbers",
ajax: {
url: "/client/api/v1/departments/",
data: function(d) {
d.limit = d.length;
d.offset = d.start;
d.dept_name__icontains = d.search.value;
},
dataSrc: function(json) {
for (var i=0, len=json.objects.length ; i<len ; i++) {
json.objects[i].DT_RowId = json.objects[i].dept_id;
}
return json.objects;
}
},
aLengthMenu: [
[5, 25, 50, 100],
[5, 25, 50, 100]
],
iDisplayLength: 5,
columns: [
{
data: "dept_name"
},
{
data: "dept_created_on",
render: function ( data, type, full, meta ) {
var dateCreated = new Date(data);
dateCreated = dateCreated.toLocaleDateString();
return dateCreated;
}
}
]
});
Any help will be appreciated.
Thanks in Advance :)
You can pass a function to the DataTables ajax option, this will give you full control over how to fetch and map the data before passing it to DataTables.
.DataTable({
serverSide: true,
ajax: function(data, callback, settings) {
// make a regular ajax request using data.start and data.length
$.get('/client/api/v1/departments/', {
limit: data.length,
offset: data.start,
dept_name__icontains: data.search.value
}, function(res) {
// map your server's response to the DataTables format and pass it to
// DataTables' callback
callback({
recordsTotal: res.meta.total_count,
recordsFiltered: res.meta.total_count,
data: res.objects
});
});
}
});
The solution above has been tested with jQuery DataTables 1.10.4.
As this question is tagged with Angular, here's a solution for those using angular-datatables.
<div ng-controller="testCtrl">
<table datatable dt-options="dtOptions" dt-columns="dtColumns" class="row-border hover"></table>
</div>
.controller('testCtrl', function($scope, $http, DTOptionsBuilder, DTColumnBuilder) {
$scope.dtOptions = DTOptionsBuilder.newOptions()
.withOption('serverSide', true)
.withOption('ajax', function(data, callback, settings) {
// make an ajax request using data.start and data.length
$http.get('/client/api/v1/departments/', {
limit: data.length,
offset: data.start,
dept_name__icontains: data.search.value
}).success(function(res) {
// map your server's response to the DataTables format and pass it to
// DataTables' callback
callback({
recordsTotal: res.meta.total_count,
recordsFiltered: res.meta.total_count,
data: res.objects
});
});
})
.withDataProp('data'); // IMPORTANT¹
$scope.dtColumns = [
// your column definitions here
];
});
The solution above has been tested with angular-datatables 0.3.0 + DataTables 1.10.4.
¹ The important part to note here is that the angular-datatables solution requires .withDataProp('data') to work, while the pure jQuery DataTables solution does not have a data: 'data' option.
This answer was very useful, but seems a bit outdated in the context of the current (1.10.12 at the moment) version of datatables, which actually makes life a lot easier (or at least more readable).
Under the current version you can do something like the following in your declaration (bearing in mind that tastypie needs to have the filterable & ordering options set for the fields you want to use).
You can now access the data being submitted in the ajax request by doing data.attr inside the function.
This assumes you wish to restrict searching to one field, but can easily be extended in the same manner do console.log(data) within the ajax function to see what is sent.
var table = $('#tableName').DataTable({
"deferRender":true,
"serverSide": true,
"ajax": function(data, callback, settings) {
var sort_column_name = data.columns[data.order[0].column].data.replace(/\./g,"__");
var direction = "";
if (data.order[0].dir == "desc") { direction = "-"};
$.get('your/tasty/pie/url?format=json', {
limit: data.length,
offset: data.start,
your_search_field__searchattr: data.search.value,
order_by: direction +sort_column_name
}, function(res) {
callback({
recordsTotal: res.meta.total_count,
recordsFiltered: res.meta.total_count,
data: res.objects
});
});
},
"columns": [
{ "data":"column_1", "orderable": false },
{ "data":"column_2" },
{ "data":"column_3" }
],
"order": [[1, "asc"]]
});

Dynamic Flot chart using Ajax

I'm new to javascript and flot and hope someone can help me with this problem I'm having.
What I'm trying to achieve it have a dynamic flot graph on a page.
The data for the series that is to be plotted is in a file that will get updated every few seconds. I want the flot graph to read the data file every few seconds and be updated with the new data.
Here's the code I've got which isn't working. The graph displays ok on page load, but just doesn't get updated every 5 seconds.
Any help is much appreciated.
$(function () {
var dataFolder = "http://localhost/graphdata/";
/***********************************************************************
* Function to get series data from a file
***********************************************************************/
function getSeriesData(file) {
var url= dataFolder + file;
var data = null;
$.ajax({
async: false,
url: url,
method: 'GET',
dataType: 'json',
success: function(datasets){
data = datasets;
},
error: function(error,text,http){
alert(error + " " + text + " " + http);
}
});
return data;
}
var plot = $.plot($("#placeholder"),
[
{label: "A", data: getSeriesData("dataA.txt")},
{label: "B", data: getSeriesData("dataB.txt")},
{label: "C", data: getSeriesData("dataC.txt")}
],
{
series: {
lines: {
color: "red",
show: true
},
points: {
show: true
},
shadowSize: 0,
hoverable: true
},
colors: ["red", "blue", "green"],
yaxis: {
min: 0, ticks:5
},
xaxis: {
mode: 'time',
timeformat: '%H:%m',
show: false
},
legend:{
show: true
},
grid:{
color: "green",
show: true,
backgroundColor: "white",
hoverable: true
}
}
);
var updateInterval = 1000 * 5;
function update() {
plot.setData([
{label: "A", data: getSeriesData("dataA.txt")},
{label: "B", data: getSeriesData("dataB.txt")},
{label: "C", data: getSeriesData("dataC.txt")}
]);
plot.setupGrid();
plot.draw();
setTimeout(update, updateInterval);
}
update();});
I figured it out. The ajax call for the file was being cached in the browser so any further calls for the same file would have been returned from cache, making it look like no updates to the graph were happening. Switch caching of in the function and it works fine now.
function getSeriesData(file) {
var url= dataFolder + file;
var data = null;
$.ajax({
async: false,
cache: false,
url: url,
method: 'GET',
dataType: 'json',
success: function(datasets){
data = datasets;
},
error: function(error,text,http){
alert(error + " " + text + " " + http);
}
});
return data;
}
It seems to update correctly for me using numbers. Can you provide a few data examples?
I changed the x axis and data pull but the updating worked fine.
function getSeriesData() {
var randomnumber=Math.floor(Math.random()*11)
var randomnumber2=Math.floor(Math.random()*11)
var data = [
[randomnumber, randomnumber2],
[randomnumber +1, randomnumber2 +2],
[randomnumber +3, randomnumber2 +4],
[randomnumber +5, randomnumber2 +6],
[randomnumber +7, randomnumber2 +8],
];
return data;
}
fiddle - http://jsfiddle.net/EX6dv/1/
It seems you need to put following out side the update function too.
setTimeout(update, updateInterval);

Ajax JQuery responses in a specific order

I'm using an AJAX request (with jQuery) to retrieve data from a XML file. I was wandering what is the best way to sort the result in a specific order before printing them on the page.
the problem is that I'm calculating a distance between a starting position and the
position of every item in the XML and then append that distance (in jQuery) to each item.
Everything is working except the items are listed on the page related to their order in the original XML.
Now, what I would like to do is to sort this list from the smallest distance to the biggest before adding them to the page...
Is there any sort of "sort-by" of "order-by" function in jQuery/AJAX (or does that make any sense)?
So far, here's what I have:
$.ajax({
type: "GET",
url: "blc.xml",
dataType: "xml",
success: parseXml
});
function parseXml(xml) {
$(xml).find("marker").each(function() {
var transit = $(this).find("transit").text();
var type = $(this).find("type").text();
var codepostal = $(this).find("codepostal").text();
var lat2 = $(this).find("lat").text();
var lng2 = $(this).find("lng").text();
var maxDist = 10;
if (newLatLon < maxDist) {
$("#list").append('<p id="' + transit + '">' + type + codepostal + '</p>');
$("#" + transit).append(document.createTextNode(" " + newLatLon + " KM"));
}
Thanks for your input!
(Please not that I don't want to be able to drag items in any order (as with jQuery UI), just to print them in a specific order).
Tablesorter is a jquery plug-in for building sortable tables. There are many, take your pick.
Can you just use the JavaScript function sort()?
function sortNumber(a,b)
{
return a - b;
}
var n = [10, 5, 40, 25, 100, 1];
var sorted = n.sort(sortNumber)
// sorted = [1, 5, 10, 25, 40, 100]
EDIT: Added sortNumber function.

How to refresh jqplot bar chart without redrawing the chart

I have a jqplot bar chart and I want the chart data to be changed when the user changes the value on a drop-down list. That works, but the problem is the bar chart redraws, one over another, each time the user changes the values.
How can I update or reload the bars without drawing the whole thing again? Is there any property value to be set?
Chart data changes according to an ajax call:
$.ajax({
url: '/Home/ChartData',
type: 'GET',
data: { Id: Id },
dataType: 'json',
success: function (data) {
$.jqplot('chartDiv', [a, b], CreateBarChartOptions(xAxis));
}});
function CreateBarChartOptions(xAxis) {
var optionsObj = {
title: 'Stat',
axes: {
xaxis: {
renderer: $.jqplot.CategoryAxisRenderer,
ticks: xAxis
},
yaxis: { min: 0 }
},
series: [{ label: 'A' }, { label: 'B'}],
seriesDefaults: {
shadow: true,
renderer: $.jqplot.BarRenderer,
rendererOptions: {
barPadding: 8,
barMargin: 10
}
},
};
return optionsObj;
}
A reply would be highly appreciated. Thanks.
What you want to do is call jqPlot's .replot() method when you draw the new chart. Change your ajax call to look like this:
$.ajax({
url: '/Home/ChartData',
type: 'GET',
data: { Id: Id },
dataType: 'json',
success: function (data) {
$.jqplot('chartDiv', [a, b], CreateBarChartOptions(xAxis)).replot();
}});
Try having your chart object as a global variable in your script as:
var plot1 = $.jqplot('chartDiv', [a, b], CreateBarChartOptions(xAxis));
Then on success reset the data, axesScale and replot as:
var newData = [['a',1],['b',2],['c',3]];
plot1.series[0].data = newData;
plot1.resetAxesScale();
plot1.replot();
Ref: https://groups.google.com/group/jqplot-users/browse_thread/thread/59df82899617242b?pli=1
Each time before redrawing the graph, just destroy the existing1.
$.ajax({
url: '/Home/ChartData',
type: 'GET',
data: { Id: Id },
dataType: 'json',
success: function (data) {
if(plot)
{
plot.destroy();
}
var plot=$.jqplot('chartDiv', [a, b], CreateBarChartOptions(xAxis));
}});
Took me a while to find an answer for the script generated data, so I'm going to post this right here. I used a combination of the above code.
I created a global var, named plot3 within my script file. Then created the below function. When this is called with a redraw boolean, it determines if I need to destroy and redraw or draw for the first time.
What first bit of code does is gets data from my jqgrid, (which is being updated in a different function), and updates the arrays. The second bit, determines my interval ticks, on the x-axis dependent upon my length of data.
function DrawGraph(bRedraw){
var testTimes = [];
testTimes = $('#polarizationTable').jqGrid('getCol', 'TestTime', testTimes, false);
var RdgA = $('#polarizationTable').jqGrid('getCol', 'RdgA', RdgA, false);
var RdgB = $('#polarizationTable').jqGrid('getCol', 'RdgB', RdgB, false);
var readingLineA = [];
for (var i=0; i<testTimes.length; i++){
readingLineA.push([testTimes[i], RdgA[i]]);
}
var readingLineB = [];
for (var i=0; i<testTimes.length; i++){
readingLineB.push([testTimes[i], RdgB[i]]);
}
var maxX = $("#testLength").val();
var lengthX = testTimes.length;
var tickIntervalX = Math.round(maxX/10);
if(bRedraw == true)
{
plot3.destroy();
bRedraw = false;
}
if(bRedraw == false)
{
plot3 = $.jqplot('chart3', [readingLineA, readingLineB],
{
title:'Graph',
series:[{label:'Reading - A'}, {label:'Reading - B'} ],
legend:{show:true, location:'se'},
// You can specify options for all axes on the plot at once with
// the axesDefaults object. Here, we're using a canvas renderer
// to draw the axis label which allows rotated text.
axes:{
xaxis:{
label:'Minutes',
syncTicks: true,
min: 0,
numberTicks: 10,
tickInterval: tickIntervalX,
max: maxX*1.1,
labelRenderer: $.jqplot.CanvasAxisLabelRenderer,
labelOptions: {
fontSize: '12pt'
},
},
yaxis:{
label:'Data',
min: 0,
numberTicks: 10,
labelRenderer: $.jqplot.CanvasAxisLabelRenderer,
labelOptions: {
fontSize: '12pt'
}
},
}
});
}
}
Here is a full example of how to dynamically update the plot with new data without reloading the page:
<div id="chart1" style="height: 300px; width: 500px; position: relative;"></div>
<button>New data point</button>
<script type="text/javascript">
var storedData = [3, 7];
var plot1;
renderGraph();
$('button').click( function() {
doUpdate();
});
function renderGraph() {
if (plot1) {
plot1.destroy();
}
plot1 = $.jqplot('chart1', [storedData]);
}
function doUpdate() {
var newVal = Math.random();
storedData.push(newVal);
renderGraph();
}
</script>
It's a simplified version of this guy's post: JQPlot auto refresh chart with dynamic ajax data
$('#chart).html('');
chart is the DIV where chart is created.
this does the trick, nothing fancy by effective.
Maybe this will help. I on the other hand is having problem with getting replot to work at all, but i'am using a dataRenderer.
$.ajax({
url: '/Home/ChartData',
type: 'GET',
data: { Id: Id },
dataType: 'json',
success: function (data) {
$('chartDiv').empty();
$.jqplot('chartDiv', [a, b], CreateBarChartOptions(xAxis));
}});
hope this helps
jQuery(document).ready(function(){
jQuery.ajax({
url: '/review_graphs/show',
type: 'GET',
success: function (data) {
var plot1 = jQuery.jqplot('chartDiv', [data,data],
{
title: 'Bianual Reviews percentage',
series:[
{
renderer:jQuery.jqplot.BarRenderer,
label:'Average',
stackSeries: true,
dragable: {color: '#ff3366',constrainTo: 'x'},
trendline:{show: false}
},
{
label:'Trend Line',trendline:{show: false}}
],
legend: {
show: true,
placement: 'outsideGrid'
},
axesDefaults: {
tickRenderer: jQuery.jqplot.CanvasAxisTickRenderer ,
tickOptions: {
angle: -30,
fontSize: '10pt'
}
},
axes: {
xaxis: {
renderer: jQuery.jqplot.CategoryAxisRenderer
}
}
});
}});
});
The best method I got is, the div in which you're drawing, clear that before you draw your new graph.
$('#graph_area).children().remove();

How to get the Jquery Autocomplete result event handler to work?

I wrote code that fails to use the JQuery autocomplete to fire a result function after the user selects something valid (below).
By result, I mean result handler, a function that fires after a good selection happens in the autocomplete plugin. Documented here.
In my case, I have a form that is really a table where each row is the same, minus the unique ids on the fields: Item1, Qty1, Desc1, then Item2, Qty2, Desc2, and so on. When the user types in an Item1 code, the Desc1 text should display the English of the item code selected (Item2 -> Desc2, and so on).
I used this code to find all the Item inputs and slap the autocomplete on it. The result event handler doesn't fire for some reason. If you notice, I hard coded the "Item1" selection because I haven't figured out how to select the corresponding Desc to the Item where Item1 -> Desc1, Item2 -> Desc2, and so on.
I used the MVC Url.Content only because I happen to get it working. Someone used Url.Action, which I think is better, just have to figure it out.
Feel free to correct my use as necessary, this is my first time with ASP.NET MVC / JQuery.
Thank you :)
The code:
$(document).ready(function(){
$("input[id^='Item']").autocomplete( "<%= Url.Content("~/products/autocomplete")%>", {
dataType: 'json',
parse: function(data) {
var rows = new Array();
for( var i = 0; i<data.length; i++)
{ rows[i] = {data:data[i], value:data[i].name, result:data[i].id }; }
return rows;
},
formatItem: function(row, i, n) {
return row.id + ' - ' + row.name;
},
selectFirst: true,
//autoFill: true,
minChars: 2,
max: 30,
autoFill: true,
mustMatch: true,
matchContains: false,
scrollHeight: 100,
width: 300,
cacheLength: 1,
scroll: true
});
$("Item1").result(function(event, data, formatted) {
$("Desc1").html( !data ? "No match!" : "Selected: " + formatted);
});
});
$(document).ready(function(){
$("input[id^='Item']").autocomplete( "<%= Url.Content("~/products/autocomplete")%>", {
dataType: 'json',
parse: function(data) {
var rows = new Array();
for( var i = 0; i<data.length; i++)
{ rows[i] = {data:data[i], value:data[i].name, result:data[i].id }; }
return rows;
},
formatItem: function(row, i, n) {
return row.id + ' - ' + row.name;
},
selectFirst: true,
//autoFill: true,
minChars: 2,
max: 30,
autoFill: true,
mustMatch: true,
matchContains: false,
scrollHeight: 100,
width: 300,
cacheLength: 1,
scroll: true
}).result(function(event, data, formatted) {
var n = $(this).attr("id").match(/\d+/);
var b = $("span[id='Desc"+n+"']")
b.html( !data ? "No match!" : "Selected: " + formatted);
});
});

Resources