error setting a datatable option that points to a javascript function

1,557 views
Skip to first unread message

Dan Tenenbaum

unread,
Jan 7, 2014, 9:03:47 PM1/7/14
to shiny-...@googlegroups.com
Hi,


If I pass an option to renderDataTable that refers to a javascript function, I get the javascript error:

Uncaught TypeError: Object myCallbackFunction has no method 'apply'


For example,
If I have /tmp/demo.js that contains the following:

console.log("here we are in demo.js");
var myCallbackFunction = function( nRow, aData, iDisplayIndex ) {
console.log("in row callback function");
    return nRow;
}


Then my R code like this (based on shiny datatables demo at 

library(shiny)

addResourcePath("js", "/tmp")

runApp(list(
  ui = basicPage(
    tagList(singleton(
      tags$script(src="js/demo.js")
      )),
    h2('The mtcars data'),
    dataTableOutput('mytable')
  ),
  server = function(input, output) {
    output$mytable = renderDataTable({
      mtcars
    },
      options=list(
          fnRowCallback="myCallbackFunction"
        )
    )
  }
))

If I run the R code, with the javascript console visible, I see:

here we are in demo.js demo.js:1
event.returnValue is deprecated. Please use the standard event.preventDefault() instead.
Uncaught TypeError: Object myCallbackFunction has no method 'apply'


My guess is that shiny is creating some javascript that looks like this:

fnRowCallback: "myCallbackFunction"

But I want myCallbackFunction to not be in quotes because it is a function. 

I tried changing the options to this (ugly):


options=list(
          fnRowCallback="function(nRow,aData,iDisplayIndex) {console.log('hifromfunc');return nRow;}"
        )
    )

But that results in:

Uncaught TypeError: Object function(nRow,aData,iDisplayIndex) {console.log('hifromfunc');return nRow;} has no method 'apply'


Is there a way to pass a reference to an already-defined javascript function as an option to renderDataTable()?

Thanks,
Dan

Yihui Xie

unread,
Jan 7, 2014, 10:45:25 PM1/7/14
to Dan Tenenbaum, shiny-discuss
This is an FAQ about datatables now. The problem is that JSON does not
allow functions (www.json.org), so there is no "official" way of
passing functions from R to JS. Ramnath's workaround is to use a
special character string like "#! function()... !#". I do not know if
we should follow this notion.

You can update the settings of datatables after the initialization,
although the approach is kind of awkward (retrieve the settings,
destroy the table, and re-initialize with new settings):
http://stackoverflow.com/questions/13912363/jquery-datatables-change-rowcallback-after-initialisation

Regards,
Yihui

Dan Tenenbaum

unread,
Jan 7, 2014, 11:03:14 PM1/7/14
to shiny-...@googlegroups.com, Dan Tenenbaum


On Tuesday, January 7, 2014 7:45:25 PM UTC-8, Yihui Xie wrote:
This is an FAQ about datatables now. The problem is that JSON does not
allow functions (www.json.org), so there is no "official" way of
passing functions from R to JS. Ramnath's workaround is to use a
special character string like "#! function()... !#". I do not know if
we should follow this notion.


How about: list(myfunc = expression("myfunc})?

Then anything where is.expression() == TRUE is treated as a function.

This is REALLY a huge problem. Please fix it! It means R users can't affect any of the datatables settings which require a function. That cripples us for no good reason.


 
You can update the settings of datatables after the initialization,
although the approach is kind of awkward (retrieve the settings,
destroy the table, and re-initialize with new settings):
http://stackoverflow.com/questions/13912363/jquery-datatables-change-rowcallback-after-initialisation


OK, in the meantime I'll do this, but I'm not sure how to trigger the update of settings. I can't put it in the document ready method because presumably shiny is putting the first call to datatable() there. So I somehow have to figure out when the document ready method has finished and then call my function. Any ideas how to do that?

Thanks,
Dan

Dan Tenenbaum

unread,
Jan 7, 2014, 11:55:14 PM1/7/14
to shiny-...@googlegroups.com, Dan Tenenbaum
Hi Yihui,

The workaround you suggest does not work.

Here is an example. Replace the earlier JS file with this one:

console.log("here we are in demo.js");
var myCallbackFunction = function( nRow, aData, iDisplayIndex ) {
console.log("in row callback function");
    return nRow;
}


$(function() {
  console.log("in document ready function");
});

// this won't be run until the document ready function is finished:
$(window).load(function(e) {
console.log("in window load function");


    var oTable = $('#DataTables_Table_0').dataTable();
    console.log("oTable is " );
    console.log(oTable);


var newoptions = {"fnRowCallback": myCallbackFunction};


oTable.fnDestroy(); // this throws an error

oTable = $('#DataTables_Table_0').dataTable(newoptions);
});



First of all notice that I couldn't say 

 var oTable = $('#mytable').dataTable();

I had to say 

 var oTable = $('#DataTables_Table_0').dataTable();

Then when I try and call oTable.fnDestroy(), I get this:

Uncaught TypeError: Cannot read property 'nTableWrapper' of null 

I tried commenting out the fnDestroy line altogether but that didn't work.

I really need a solution for this! 

What about doing something a la Rchart? Javascript's JSON parser takes a reviver argument that will transform certain values as it parses, so it could take something like
"function:myfunc" and turn it into a reference to a function called myfunc. 


For the moment I'm more interested in a workaround that will work immediately. 


Thanks!

Dan

Dan Tenenbaum

unread,
Jan 8, 2014, 12:00:22 AM1/8/14
to shiny-...@googlegroups.com, Dan Tenenbaum
Oh, also for this you need to remove the options from the call to renderDataTable() in the R code.
Dan

Yihui Xie

unread,
Jan 8, 2014, 12:04:20 AM1/8/14
to Dan Tenenbaum, shiny-discuss
Yes, it is indeed a huge problem. As I said, the fundamental problem
was from the design of JSON, which just does not allow function
objects. There is basically only one way to go, which is eval(). The
question is how to determine the elements that should be evaluated.
rCharts uses its home-made "#!...!#" notation as a workaround. Another
question is whether eval() can lead to security problems. I have been
thinking for quite a while on this issue, and I guess we will come up
with a solution soon.

Regards,
Yihui

Yihui Xie

unread,
Jan 8, 2014, 12:06:31 AM1/8/14
to Dan Tenenbaum, shiny-discuss
The first time you generate the table, you need
options=list(bRetrieve=TRUE) so that you can retrieve the table object
later. I guess that is the problem.

Regards,
Yihui

Dan Tenenbaum

unread,
Jan 8, 2014, 12:11:59 AM1/8/14
to shiny-...@googlegroups.com, Dan Tenenbaum
I tried that. Same error.
Dan

Dan Tenenbaum

unread,
Jan 8, 2014, 1:05:57 AM1/8/14
to shiny-...@googlegroups.com, Dan Tenenbaum
I finally got it to work by using a very awful workaround. I made a copy of shiny.js called shiny2.js with the following difference:

--- /Users/dante/Downloads/shiny/inst/www/shared/shiny.js 2013-10-29 11:36:43.000000000 -0700
+++ shiny2.js 2014-01-07 21:53:53.000000000 -0800
@@ -1,3 +1,4 @@
+console.log("in shiny2.js");
 /*jshint browser:true, jquery:true, strict:false, curly:false, indent:2*/
 
 (function() {
@@ -1214,7 +1215,8 @@
         "aaSorting": [],
         "bSortClasses": false,
         "iDisplayLength": 25,
-        "sAjaxSource": data.action
+        "sAjaxSource": data.action,
+        "fnRowCallback": myCallbackFunction
       }, data.options));
       // use debouncing for searching boxes
       $el.find('label input').first().unbind('keyup')


And loaded that after shiny loads shiny.js.

I admit that it's horrible, but it works. I'll do this until there is a better way.

Please fix this and save me from the horrible workaround!
Thanks,
Dan

Yihui Xie

unread,
Jan 8, 2014, 1:30:13 AM1/8/14
to Dan Tenenbaum, shiny-discuss
Just FYI, I proposed this approach:
https://github.com/rstudio/shiny/pull/356 I'm not sure if it will go
to the release version eventually, but it certainly works for your
case.

Regards,
Yihui

Dan Tenenbaum

unread,
Jan 8, 2014, 1:35:58 AM1/8/14
to shiny-...@googlegroups.com, Dan Tenenbaum
This is great! 
I like the approach, which does not require any special syntax.
Hope it makes it into release. 

Dan
Reply all
Reply to author
Forward
0 new messages