Recommended workflow for tablesorter serverSideSorting? - tablesorter

Question for Mottie's tablesorter version 2.30.5.
What is the recommended workflow for serverSideSorting? More specifically, when / how do I apply / insert / update the table data coming back from the server such that I don't wipe out information like last.sortList?
I'm following Mottie's answer here as close as I can but I am unable to sort on multiple columns, because when I reinitialize tablesorter, it clears last.sortList.
jQuery("#search_results_table")
.on("sortEnd", function(e, table) {
jQuery('#search_sort').val(JSON.stringify(table.config.sortList));
// yuck
if (gTableSorterReady) {
// call server here, which calls this code on completion
}
})
.on("tablesorter-ready", function(e) {
// yuck
gTableSorterReady = true;
})
.tablesorter({
theme : 'search-theme',
widgets : [ 'zebra', 'stickyHeaders' ],
widthFixed : false,
widgetOptions : {
stickyHeaders_attachTo: '#search_results_container',
},
cssAsc : "headerSortUp",
cssDesc : "headerSortDown",
cssHeader : "header",
sortList : inSortList,
serverSideSorting: true
});

Related

SAPUI5 filter Table dynamically on initialization with odata v4

I created a view in sap hana that I expose using odata v4 and this part is working well.
Now I'm trying to add a table in the details page of a worklist using the data from my view filtered by the ID of the object I display in the details page.
What I have tried so far is to do in directly in XML by doing that :
<Table id="historyTable"
inset="false"
items="{
path: '/wfl_comment',
parameters : {
$filter: 'INSTANCE_ID eq ${ID}'
}
}">
But as I found in SAPUI5 documentation it's not possible to do it dynamically in the XML.
So I tried to filter in the controller following this documentation and this sample from SAPUI5 by adapting onSearch function of the sample in my onInit function.
My XML:
<Table id="historyTable"
inset="false"
items="{
path: '/wfl_comment'
}">
My controller.js
onInit : function () {
var oViewModel = new JSONModel({
busy : true,
delay : 0,
synchronizationMode : "None"
});
this.getRouter().getRoute("object").attachPatternMatched(this._onObjectMatched, this);
this.setModel(oViewModel, "objectView");
let oView = this.getView();
let sValue = oView.byId("ID").getValue();
let oFilter = new Filter("INSTANCE_ID", FilterOperator.EQ, sValue);
oView.byId("historyTable").getBinding("items").filter(oFilter, FilterType.Application);
}
Here because of the instantiation of oFilter I get the following error :
The following error occurred while displaying routing target with name 'object': Error: Synchronization mode must be 'None' -
But in my manifest.json I set Synchronization mode to None. Here is the models:
"models": {
"i18n": {
...
}
},
"": {
"dataSource": "mainService",
"preload": true,
"settings": {
"operationMode": "Server",
"groupId": "$direct",
"synchronizationMode": "None",
"autoExpandSelect":true
}
}
}
I also tried to set a static value instead of sValue when I instantiate oFilter but it's not working either.
How can I fix this error ? and is it the right way to filter the data for a Table that I only want to filter once during the initialization ?

How to force a refresh of Ajax data

I'm using the Ajax pager and I have some code that adds a record to my database. What I want to do is force the records to be refreshed. I'm trying to use $("builders_table").trigger("update"), but that doesn't work. If I change pages or filter the records, then the updated records are returned, but I would like to force a refresh as soon as the database is changed.
Thanks
$('#builders_table')
.tablesorter({
theme: 'blue',
widthFixed: true,
cancelSelection: false,
sortLocaleCompare: true, // needed for accented characters in the data
sortList: [ [1,1] ],
widgets: ['zebra', 'filter']
})
.tablesorterPager({
container: $('.pager'),
ajaxUrl : '/builder_data.php?page={page}&size={size}&{filterList:filter}&{sortList:column}',
// use this option to manipulate and/or add additional parameters to the ajax url
customAjaxUrl: function(table, url) {
// manipulate the url string as you desire
//url += url_extras;
// trigger a custom event; if you want
$(table).trigger('changingUrl', url);
// send the server the current page
return url;
},
ajaxError: null,
ajaxObject: {
dataType: 'json'
},
ajaxProcessing: function(data){
if (data && data.hasOwnProperty('rows')) {
return [ data.total_rows, $(data.rows) ];
}
},
// Set this option to false if your table data is preloaded into the table, but you are still using ajax
processAjaxOnInit: true,
initialRows: {
// these are both set to 100 in the ajaxProcessing
// the these settings only show up initially
total: 50,
filtered: 50
},
output: '{startRow} to {endRow} ({totalRows})',
updateArrows: true,
page: 0,
size: 50,
savePages: false,
storageKey: 'tablesorter-pager',
pageReset: 0,
fixedHeight: false,
removeRows: false,
countChildRows: false,
// css class names of pager arrows
cssNext : '.next', // next page arrow
cssPrev : '.prev', // previous page arrow
cssFirst : '.first', // go to first page arrow
cssLast : '.last', // go to last page arrow
cssGoto : '.gotoPage', // page select dropdown - select dropdown that set the "page" option
cssPageDisplay : '.pagedisplay', // location of where the "output" is displayed
cssPageSize : '.pagesize', // page size selector - select dropdown that sets the "size" option
// class added to arrows when at the extremes; see the "updateArrows" option
// (i.e. prev/first arrows are "disabled" when on the first page)
cssDisabled : 'disabled', // Note there is no period "." in front of this class name
cssErrorRow : 'tablesorter-errorRow' // error information row
});
The pager has a built-in method to force an update named "pagerUpdate":
You can force an update as follows:
$('table').trigger('pagerUpdate');
or, if you want to force an update and change the page
$('table').trigger('pagerUpdate', 3); // update and set to page 3

Trying to use jqplot with jquery mobile, marionette and requirejs

I am trying to use jqplot with Jquery mobile, marionette and requirejs. I have included all jqplot required CSS as well as script files in head tags, but when i am trying to plot a chart using below code
define([ 'jquery', 'plot' ],
function($) {
console.log("Success..Inside Offer Page Script.");
console.log("Plot..."+$.jqplot);
console.log("jquery..."+$);
$.jqplot.config.enablePlugins = true;
var s1 = [ 2, 6, 7, 10 ];
var ticks = [ 'a', 'b', 'c', 'd' ];
plot1 = $.jqplot('chart1', [ s1 ], {
seriesDefaults : {
renderer : $.jqplot.BarRenderer,
pointLabels : {
show : true
}
},
axes : {
xaxis : {
renderer : $.jqplot.CategoryAxisRenderer,
ticks : ticks
}
},
highlighter : {
show : false
}
});
});
it gives me errors like
Uncaught TypeError: undefined is not a function jqplot.barRenderer.js:41
(line 41: $.jqplot.BarRenderer.prototype = new $.jqplot.LineRenderer();)
Uncaught TypeError: Cannot call method 'push' of undefined jqplot.pointLabels.js:377
(line 377: $.jqplot.postSeriesInitHooks.push($.jqplot.PointLabels.init);)
The plot in my above code's define is
define([
'../scripts/ext_libs/jquery.jqplot'
],
function () {
var plot;
require([
'../scripts/ext_libs/jqplot.barRenderer',
'../scripts/ext_libs/jqplot.pointLabels',
'../scripts/ext_libs/jqplot.categoryAxisRenderer',
],
function () {
plot = $.jqplot;
});
return plot;
});
Can anyone please help me how can i solve these errors. Is their a problem using jqplot with requirejs?
Thanks in advance.
I'm not using marionette, but everything else works fine on my side. I have a jqplot module like this:
define([
'../js/plugins/jqplot/jquery.jqplot'
, 'css!../js/plugins/jqplot/jquery.jqplot'
],
function () {
var plot;
require([
'../js/plugins/jqplot/plugins/jqplot.barRenderer'
, '../js/plugins/jqplot/plugins/jqplot.logAxisRenderer'
, '../js/plugins/jqplot/plugins/jqplot.categoryAxisRenderer'
, '../js/plugins/jqplot/plugins/jqplot.canvasAxisTickRenderer'
, '../js/plugins/jqplot/plugins/jqplot.canvasTextRenderer'
, '../js/plugins/jqplot/plugins/jqplot.pointLabels'
, '../js/plugins/jqplot/plugins/jqplot.enhancedLegendRenderer'
],
function () {
plot = $.jqplot;
});
return plot;
}
);
which I'm requiring requiring normally from my page script like this:
require(["plot"], function (plot) {
// do something here with plot or... $.jqplot
};
You should be able to use $.plot right away, because once you require the module, jqplot will be available on $.
EDIT:
Try this:
define([ 'jquery', 'plot' ],
function($) {
console.log("Success..Inside Offer Page Script.");
console.log($);
console.log($.jqplot);
$.jqplot.config.enablePlugins = true;
var s1 = [ 2, 6, 7, 10 ];
var ticks = [ 'a', 'b', 'c', 'd' ];
plot1 = $.jqplot('chart1', [ s1 ], {
seriesDefaults : {
renderer : $.jqplot.BarRenderer,
pointLabels : {
show : true
}
},
axes : {
xaxis : {
renderer : $.jqplot.CategoryAxisRenderer,
ticks : ticks
}
},
highlighter : {
show : false
}
});
});
Bit late to the game but.... above doesn't work because require is async to return, so able to return jqplot without any jqplot plugins loaded! async safe solution below
Nasty problem, as it's a chain of three dependencies
jquery is required for jqplot which is required for jqplot plugins, I have a simpler solution based on the same lines as the one above
first do your requirejs "main.js" config like so
requirejs.config({
paths: {
"jquery": "path/to/jquery-1.10.2.min",
// WORKAROUND : jQuery plugins + shims
"jqplot.core": "path/to/jquery-jqplot-1.0.8.min",
"jqplot": "jquery-jqplot-module-with-plugins-1.0.8"
},
shim: {
"jqplot.core": {deps: ["jquery"]},
"jqplot": {deps: ["jqplot.core"]}
}
});
create a wrapper file module file called "jquery-jqplot-module-with-plugins-1.0.8.js", containing :
// wraps jquery jqplot plugin in define statement
define([
"jquery",
"path/to/jqplot.highlighter.min",
"path/to/jqplot.cursor.min",
"path/to/jqplot.dateAxisRenderer.min",
"path/to/jqplot.canvasTextRenderer.min",
"path/to/jqplot.canvasAxisLabelRenderer.min",
"path/to/jqplot.enhancedLegendRenderer.min",
"path/to/jqplot.pieRenderer.min",
"path/to/jqplot.donutRenderer.min",
], function($) {
var jqplot;
jqplot = $.jqplot;
return jqplot;
});
Then when ever you need jqplot with those plugins, simply call for "jqplot" which will load "jquery" then "jqplot.core" then all the jqplot modules, then finally return the jqplot object :)
require(["jquery", "jqplot"], function ($, $jqplot) {
console.log("Success..Inside Require JS");
console.log("Plot...", $.jqplot, $jqplot);
});
or
define(["jquery", "jqplot"], function ($, $jqplot) {
console.log("Success..Inside Define JS");
console.log("Plot...", $.jqplot, $jqplot);
});
tada! :)
ps jquery plugins are evil, no suggestion how to fix that situation, just a statement of fact
cheers
Ant
Looks like plot is returned before require(...) initializes it. I used to have common solution and my plot was partly populated. I ended up with setting all jqplot plugins in shim and changed my `plot.js' accordingly, as was suggested here.
requirejs.config
shim: {
'jqplot': ['jquery'],
'lib/jquery/jqplot/plugins/jqplot.canvasTextRenderer': ['jqplot'],
'lib/jquery/jqplot/plugins/jqplot.pieRenderer': ['jqplot'],
'lib/jquery/jqplot/plugins/jqplot.barRenderer': ['jqplot'],
'lib/jquery/jqplot/plugins/jqplot.categoryAxisRenderer': ['jqplot'],
'lib/jquery/jqplot/plugins/jqplot.canvasAxisLabelRenderer': ['jqplot'],
'lib/jquery/jqplot/plugins/jqplot.enhancedLegendRenderer': ['jqplot'],
'lib/jquery/jqplot/plugins/jqplot.highlighter': ['jqplot'],
}
plot.js
define(['lib/jquery/jqplot/plugins/jqplot.canvasTextRenderer',
'lib/jquery/jqplot/plugins/jqplot.pieRenderer',
'lib/jquery/jqplot/plugins/jqplot.barRenderer',
'lib/jquery/jqplot/plugins/jqplot.categoryAxisRenderer',
'lib/jquery/jqplot/plugins/jqplot.canvasAxisLabelRenderer',
'lib/jquery/jqplot/plugins/jqplot.enhancedLegendRenderer',
'lib/jquery/jqplot/plugins/jqplot.highlighter'],
function() {
return $.jqplot;
});

Unable to pass additional parameters in plupload

I'm using plupload, the JQuery UI implementation. I'm trying to pass additional parameters to the server, but I can't make it work. It should be pretty straightforward, the parameters are already set when the function is executed, so that should not be a problem. I've tried this:
function GetPlUploader(m)
{
$("#divOpplaster").plupload(
{
// General settings
runtimes: 'flash,html5,silverlight',
url: 'upload.php',
max_file_size: '10mb',
chunk_size: '1mb',
unique_names: true,
multipart: true,
multipart_params: [
{
'ordre': ordreibruk,
'mode': m}
],
// Specify what files to browse for
filters: [
{
title: "Bildefiler",
extensions: "jpg,gif,png,bmp"}
],
// Flash settings
flash_swf_url: 'plupload/js/plupload.flash.swf',
// Silverlight settings
silverlight_xap_url: 'plupload/js/plupload.silverlight.xap',
init: {
FileUploaded: function(up, file, info)
{
// Called when a file has finished uploading
console.log('[FileUploaded] File:', file, "Info:", info);
}
}
});
console.log("Ordre: " + ordreibruk + ". Mode: " + m)
$("#divOpplaster").dialog(
{
autoOpen: false,
width: 650,
show: "fade",
hide: "fade"
})
$("#divOpplaster").dialog("open")
// Client side form validation
$('form').submit(function(e)
{
var uploader = $('#uploader').plupload('getUploader');
// Files in queue upload them first
if (uploader.files.length > 0)
{
// When all files are uploaded submit form
uploader.bind('StateChanged', function()
{
if (uploader.files.length === (uploader.total.uploaded + uploader.total.failed))
{
$('form')[0].submit();
}
});
uploader.start();
}
else
alert('Du må velge minst én fil for opplasting.');
return false;
});
}
I've also tried to add this in the $('form').submit section:
uploader.bind('BeforeUpload', function(up)
{
up.settings.multipart_params =
{
'ordre': ordreibruk,
'mode': m
};
});
But to no avail.
I'm sure I'm overlooking something really simple, but what?
Kind regards,
Anders
I must confess I use to put my parameters as query string parameters in the url :
during init : url: '/upload.aspx?id='+Id,
or later : upldr.settings.url = upldr.settings.url + '&token=' + myToken;
It works fine.
Hope this will help
Had the same issue. Stumbled upon this snippet that can easily translated into coffescript as well that works for my project. Allows you to pass multipart params after initialization (like in the case a field can change before the upload is hit)
var myUploader = $('#uploader').plupload('getUploader');
myUploader.bind('BeforeUpload', function(up, file) {
up.settings.multipart_params = {path : $("#path").val()};
});
Call it after you do your normal initialize and setup of $("#divOpplaster").plupload(...) (and set your ID appropriately to your uploader field, of course)

Manage multiple highchart charts in a single webpage

I am having multiple highchart charts of various types(Bar,Pie, Scatter type) in a single web page. Currently I am creating config object for each graph like,
{
chart : {},
blah blah,
}
And feeding them to a custom function which will just call HighCharts.chart(). But this results in duplication of code. I want to manage all this chart rendering logic centrally.
Any Idea on how to do this?
You can use jQuery.extend() and Highcharts.setOptions.
So first you'll make the first object which will be extended by all your charts, this object will contain your Highchart default functions.
You can do it using namespacing.
The following way is good when you have very different charts.
Default graphic:
var defaultChart = {
chartContent: null,
highchart: null,
defaults: {
chart: {
alignTicks: false,
borderColor: '#656565',
borderWidth: 1,
zoomType: 'x',
height: 400,
width: 800
},
series: []
},
// here you'll merge the defauls with the object options
init: function(options) {
this.highchart= jQuery.extend({}, this.defaults, options);
this.highchart.chart.renderTo = this.chartContent;
},
create: function() {
new Highcharts.Chart(this.highchart);
}
};
Now, if you want to make a column chart, you'll extend defaultChart
var columnChart = {
chartContent: '#yourChartContent',
options: {
// your chart options
}
};
columnChart = jQuery.extend(true, {}, defaultChart, columnChart);
// now columnChart has all defaultChart functions
// now you'll init the object with your chart options
columnChart.init(columnChart.options);
// when you want to create the chart you just call
columnChart.create();
If you have similar charts use Highcharts.setOptions which will apply the options for all created charts after this.
// `options` will be used by all charts
Highcharts.setOptions(options);
// only data options
var chart1 = Highcharts.Chart({
chart: {
renderTo: 'container1'
},
series: []
});
var chart2 = Highcharts.Chart({
chart: {
renderTo: 'container2'
},
series: []
});
Reference
http://api.highcharts.com/highcharts#Highcharts.setOptions%28%29
COMPLETE DEMO
I know this has already been answered, but I feel that it can be taken yet further. I'm still newish to JavaScript and jQuery, so if anyone finds anything wrong, or thinks that this approach breaks guidelines or rules-of-thumb of some kind, I'd be grateful for feedback.
Building on the principles described by Ricardo Lohmann, I've created a jQuery plugin, which (in my opinion) allows Highcharts to work more seamlessly with jQuery (i.e. the way that jQuery works with other HTML objects).
I've never liked the fact that you have to supply an object ID to Highcharts before it draws the chart. So with the plug-in, I can assign the chart to the standard jQuery selector object, without having to give the containing <div> an id value.
(function($){
var chartType = {
myArea : {
chart: { type: 'area' },
title: { text: 'Example Line Chart' },
xAxis: { /* xAxis settings... */ },
yAxis: { /* yAxis settings... */ },
/* etc. */
series: []
},
myColumn : {
chart: { type: 'column' },
title: { text: 'Example Column Chart' },
xAxis: { /* xAxis settings... */ },
yAxis: { /* yAxis settings... */ },
/* etc. */
series: []
}
};
var methods = {
init:
function (chartName, options) {
return this.each(function(i) {
optsThis = options[i];
chartType[chartName].chart.renderTo = this;
optsHighchart = $.extend (true, {}, chartType[chartName], optsThis);
new Highcharts.Chart (optsHighchart);
});
}
};
$.fn.cbhChart = function (action,objSettings) {
if ( chartType[action] ) {
return methods.init.apply( this, arguments );
} else if ( methods[action] ) {
return methods[method].apply(this,Array.prototype.slice.call(arguments,1));
} else if ( typeof action === 'object' || !action ) {
$.error( 'Invalid arguments to plugin: jQuery.cbhChart' );
} else {
$.error( 'Action "' + action + '" does not exist on jQuery.cbhChart' );
}
};
})(jQuery);
With this plug-in, I can now assign a chart as follows:
$('.columnChart').cbhChart('myColumn', optionsArray);
This is a simplistic example of course; for a real example, you'd have to create more complex chart-properties. But it's the principles that concern us here, and I find that this approach addresses the original question. It re-uses code, while still allowing for individual chart alterations to be applied progressively on top of each other.
In principle, it also allows you to group together multiple Ajax calls into one, pushing each graph's options and data into a single JavaScript array.
The obligatory jFiddle example is here: http://jsfiddle.net/3GYHg/1/
Criticism welcome!!
To add to #Ricardo's great answer, I have also done something very similar. In fact, I won't be wrong if i said I went a step further than this. Hence would like to share the approach.
I have created a wrapper over the highchart library. This gives multiple benefits, following being the main advantages that encouraged going in this path
Decoupling: Decouples your code from highcharts
Easy Upgrades: This wrapper will be the only code that will require modification in case of any breaking changes in highchart api after upgrades, or even if one decides to move to a differnt charting library altogether (even from highchart to highstock can be exhaustive if your application uses charts extensively)
Easy of use: The wrapper api is kept very simple, only things that may vary are exposed as options (That too whose values won't be as a deep js object like HC already has, mostly 1 level deep), each having a default value. So most of the time our chart creation is very short, with the constructor taking 1 options object with merely 4-5 properties whose defaults don't suit the chart under creation
Consistent UX: Consistent look & feel across the application. eg: tool tip format & position, colors, font family, colors, toolbar (exporting) buttons, etc
Avoid duplication: Of course as a valid answer of the asked question it has to avoid duplication, and it does to a huge extent
Here is what the options look like with their default values
defaults : {
chartType : "line",
startTime : 0,
interval : 1000,
chartData : [],
title : "Product Name",
navigator : true,
legends : true,
presetTimeRanges : [],
primaryToolbarButtons : true,
secondaryToolbarButtons : true,
zoomX : true,
zoomY : false,
height : null,
width : null,
panning : false,
reflow : false,
yDecimals : 2,
container : "container",
allowFullScreen : true,
credits : false,
showAll : false,
fontSize : "normal", // other option available is "small"
showBtnsInNewTab : false,
xAxisTitle : null,
yAxisTitle : null,
onLoad : null,
pointMarkers : false,
categories : []
}
As you can see, most of the times, its just chartData that changes. Even if you need to set some property, its mainly just true/false types, nothing like the horror that highchart constructor expects (not critizing them, the amount of options they provide is just amazing from customization Point of View, but for every developer in the team to understand & master it can take some time)
So creation of chart is as simple as
var chart=new myLib.Chart({
chartData : [[1000000,1],[2000000,2],[3000000,1],[4000000,5]]
});

Resources