使用ajax的儿童行shiny数据表

我正在尝试使用数据表库来实现更多自定义。

这是我想要做的例子。 https://datatables.net/examples/api/row_details.html请注意,我在不同的data.frame R变量中有详细信息。 像这样

A= data.frame(Name = c("Airi Satou", "Angelica Ramos","Paul Byrd") , Position = c("Accountant","Accountant", "CEO") , Office = c("Tokyo", "Tokyo", "New York")) A.detail= data.frame(Name = c("Airi Satou", "Angelica Ramos") , Extension= c("5407c", "8422") , salary = c(16000, 20000)) 

我不喜欢合并两个data.frame变量,如果可以在没有合并的情况下这样做,因为计算时间。 显然,有些行可能没有任何细节。

我可以在数据表中选择一行,并通过将其绑定为输入将行信息发送到R(感谢https://github.com/toXXIc/datatables.Selectable/ )然后我可以找到与R中所选行相关的详细信息第二个data.frame变量。 但我不知道如何将此发送回html(在所选行下)显示。 我已经把第一个表绑定为shinyoutput所以我;我不确定是否可以传递另一个数据来再次更改此输出。

也许我应该使用ajax在单击详细信息按钮时请求更多数据,但我不知道如何在shiny的情况下执行ajax请求。

在回答您的问题之前,我想指出CRAN上当前版本的Shiny(0.10.1)使用旧版本的DataTables.js(1.0.9),而您提到的示例使用DataTables.js 1.10。 DataTables 1.10中有相当大比例的API与版本1.0.9不兼容。

您可以在Github上查看此拉取请求 : https : //github.com/rstudio/shiny/pull/558 ,它提供DataTables.js 1.10支持。


首先,让我们稍微了解一下如何在Shiny中呈现数据表。

该示例使用AJAX请求从服务器URL“拉”数据,然后数据绑定到表模板。 这就是所谓的服务器端数据呈现

Shiny还使用服务器端数据呈现 。 但是,您提供的示例与Shiny之间的主要区别在于,Shiny中的数据传输对您来说是透明的。

从技术上讲,Shiny通过调用shiny:::registerDataObj()为AJAX请求创建了一个JSON API。 您可以在此处找到构建自定义AJAX请求的示例: http : //shiny.rstudio.com/gallery/selectize-rendering-methods.html 。

示例和Shiny之间的另一个区别是,它们将表内容编码为JSON blob,后者将在R代码中反映出来。 该示例使用普通对象对每一行进行编码。 例如,第一行编码为:

{ "name": "Tiger Nixon", "position": "System Architect", "salary": "$320,800", "start_date": "2011\/04\/25", "office": "Edinburgh", "extn": "5421" },

而Shiny将data.frame每一行编码为一个数组 ,例如,类似于

["Tiger Nixon", "System Architect", "$320,800", "2011\/04\/25", "Edinburgh", "5421"]

原始 JSON数据的差异会影响我们以后如何实现format()函数。

最后,该示例使用固定的HTML

模板来呈现数据表。 您可能已经注意到模板中只包含可见列(例如,扩展号列不在

模板中); 而Shiny为您创建模板,您无法决定如何执行数据绑定(例如{ "data": "name" }, )。


注意:下面的R代码使用Shiny的开发分支,您可以在上面的pull请求链接中找到它。

虽然我们无法决定将哪些列绑定到哪些数据,但是在调用DataTable()函数时,我们可以通过指定columnDefs选项来选择要隐藏的列。 您可以通过将它们包装在R中的list中来传递https://datatables.net/reference/option/中定义的任何选项。

使用示例数据的Shiny应用程序示例如下:

ui.R

 library(shiny) format.func <- "  " shinyUI( fluidPage( h5("Data table"), dataTableOutput("dt"), tags$head(HTML(format.func)) ) ) 

这里没有什么特别的,除了我已经相应地更改了format()函数,因为如前所述,Shiny将数据作为行数组而不是对象发送。

server.R

 library(shiny) library(dplyr) shinyServer(function(input, output, session) { A <- data.frame(Name = c("Airi Satou", "Angelica Ramos","Paul Byrd"), Position = c("Accountant","Accountant", "CEO"), Office = c("Tokyo", "Tokyo", "New York")) A.detail <- data.frame(Name = c("Airi Satou", "Angelica Ramos"), Extension = c("5407c", "8422"), Salary = c(16000, 20000)) # You don't necessarily need to use left_join. You can simply put every column, # including the columns you would by default to hide, in a data.frame. # Then later choose which to hide. # Here an empty column is appended to the left to mimic the "click to expand" # function you've seen in the example. A.joined <- cbind("", left_join(A, A.detail, by="Name")) columns.to.hide <- c("Extension", "Salary") # Javascript uses 0-based index columns.idx.hidden <- which(names(A.joined) %in% columns.to.hide) - 1 # Everytime a table is redrawn (can be triggered by sorting, searching and # pagination), rebind the click event. draw.callback <- " function(settings) { var api = this.api(); var callback = (function($api) { return function() { var tr = $(this).parent(); var row = $api.row(tr); if (row.child.isShown()) { row.child.hide(); tr.removeClass('shown'); } else { row.child(format(row.data())).show(); tr.addClass('shown'); } } })(api); $(this).on('click', 'td.details-control', callback); }" # wrap all options you would like to specify in options=list(), # which will be converted into corresponding JSON object. output$dt <- renderDataTable(A.joined, options=list( searching=F, columnDefs=list( list(targets=0, title="", class="details-control"), list(targets=columns.idx.hidden, visible=F) ), drawCallback=I(draw.callback) )) }) 

现在,如果您单击数据表的第一个(空)列(因为我没有编写CSS),您应该能够看到扩展区域中显示的额外信息。


编辑:延迟加载“更多详细信息”信息

上述解决方案涉及将所有信息发送到客户端,但在大多数用例中,用户可能不愿意查看隐藏信息。 基本上我们最终会向客户端发送大量冗余数据。

更好的解决方案是在Shiny中实现一个AJAX请求处理程序,它只在需要时(即用户请求时)返回信息。

要实现AJAX请求处理程序,可以使用session$registerDataObj 。 此函数在唯一的 URL处注册请求处理程序,并返回此URL。

要调用此注册请求处理程序,您需要先将此AJAX URL发送到客户端。

下面我破解了一个快速解决方案:基本上你在网页上创建了一个隐藏的元素,你可以在其上绑定一个change事件监听器。 Shiny服务器通过函数调用session$sendInputMessage向客户端发送消息来更新此元素的值。 收到消息后,它会更改元素的值,从而触发事件侦听器。 然后我们可以正确设置AJAX请求URL

之后,您可以启动任何正常的AJAX请求来获取所需的数据。

ui.R

 library(shiny) format.func <- "  " shinyUI( fluidPage( # create a hidden input element to receive AJAX request URL tags$input(id="ajax_req_url", type="text", value="", class="shiny-bound-input", style="display:none;"), h5("Data table"), dataTableOutput("dt"), tags$head(HTML(format.func)) ) ) 

server.R

 library(shiny) library(dplyr) shinyServer(function(input, output, session) { # extra more.details dummy column A <- data.frame(more.details="", Name = c("Airi Satou", "Angelica Ramos","Paul Byrd"), Position = c("Accountant","Accountant", "CEO"), Office = c("Tokyo", "Tokyo", "New York")) A.detail <- data.frame(Name = c("Airi Satou", "Angelica Ramos"), Extension = c("5407c", "8422"), Salary = c(16000, 20000)) draw.callback <- " function(settings) { var api = this.api(); var callback = (function($api) { return function() { var tr = $(this).parent(); var row = $api.row(tr); if (row.child.isShown()) { row.child.hide(); tr.removeClass('shown'); } else { // we can use the unique ajax request URL to get the extra information. $.ajax(_ajax_url, { data: {name: row.data()[1]}, success: function(res) { row.child(format(res)).show(); tr.addClass('shown'); } }); } } })(api); $(this).on('click', 'td.details-control', callback); }" ajax_url <- session$registerDataObj( name = "detail_ajax_handler", # an arbitrary name for the AJAX request handler data = A.detail, # binds your data filter = function(data, req) { query <- parseQueryString(req$QUERY_STRING) name <- query$name # pack data into JSON and send. shiny:::httpResponse( 200, "application/json", # use as.list to convert a single row into a JSON Plain Object, easier to parse at client side RJSONIO:::toJSON(as.list(data[data$Name == name, ])) ) } ) # send this UNIQUE ajax request URL to client session$sendInputMessage("ajax_req_url", list(value=ajax_url)) output$dt <- renderDataTable(A, options=list( searching=F, columnDefs=list( list(targets=0, title="", class="details-control") ), drawCallback=I(draw.callback) )) })