Jquery 使用ajax创建具有子行的数据表

Jquery 使用ajax创建具有子行的数据表,jquery,ajax,r,datatables,shiny,Jquery,Ajax,R,Datatables,Shiny,我正在尝试使用datatables库进行更多定制 这是我想举的一个例子。 请注意,我在不同的data.frame R变量中有详细信息。像这样 A= data.frame(Name = c("Airi Satou", "Angelica Ramos","Paul Byrd") , Position = c("Accountant","Accountant", "CEO") , Office = c("Tokyo", "Tokyo",

我正在尝试使用datatables库进行更多定制

这是我想举的一个例子。 请注意,我在不同的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(感谢) 然后我可以从第二个data.frame变量中找到与R中所选行相关的详细信息。 但我不知道如何将其发送回html(在所选行下)上显示。我已经按Shinyout的话装订好了第一张桌子,所以我;我不确定是否可以传递另一个数据来再次更改此输出


也许我应该在单击detail按钮时使用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:(提供DataTables.js 1.10支持)上查看此拉取请求


首先,让我们离题一点,了解数据表是如何在文本中呈现的

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

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

从技术上讲,Shinny通过调用
shinny:::registerDataObj()
为AJAX请求创建JSON API。您可以在此处找到构造自定义AJAX请求的示例:

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


{
“名字”:“老虎尼克松”,
“职位”:“系统架构师”,
“工资”:“$320800”,
“开始日期”:“2011\/04\/25”,
“办公室”:“爱丁堡”,
“extn”:“5421”
},

而Shining将
data.frame
的每一行编码为数组,例如


[“老虎尼克松”,“系统架构师”,
“$320800”、“2011\/04\/25”、“爱丁堡”、“5421”]

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

最后,示例使用固定的HTML
模板来呈现数据表。您可能已经注意到,模板中只包含可见列(例如,
模板中不包含分机号码列);然而,Shining为您创建模板,您无法决定如何执行数据绑定(例如,
{“data”:“name”},


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

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

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

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


编辑:延迟加载“更多详细信息”信息 上述解决方案涉及将所有信息发送到客户端,尽管在大多数使用情况下,用户可能不会费心查看隐藏的信息。实际上,我们最终会向客户端发送大量冗余数据

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

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

为了调用这个已注册的请求处理程序,您需要首先将这个AJAX URL发送到客户端

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

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

用户界面
库(闪亮)

format.func您可以将其作为输出对象(df或list)从服务器发回,然后在输出变量更改或设置标志时显示详细信息。如果您在github上安装现有代码,我可能会尝试这样做,并提出一个拉取请求。@Mahdi Jadaliha:我也对让它工作起来感兴趣。你有一个有效的例子吗?不幸的是,我还没有找到答案。谢谢killkeeper,它是有效的。这是否可以避免“left_join(A,A.detail,by=“Name”)”?我的桌子很大,很少使用
library(shiny)

format.func <- "
<script type='text/javascript'>
function format ( d ) {
    return '<table cellpadding=\"5\" cellspacing=\"0\" border=\"0\" style=\"padding-left:50px;\">'+
        '<tr>'+
            '<td>Full name:</td>'+
            '<td>'+d[1]+'</td>'+
        '</tr>'+
        '<tr>'+
            '<td>Extension number:</td>'+
            '<td>'+d[4]+'</td>'+
        '</tr>'+
    '</table>';
}
</script>
"

shinyUI(
    fluidPage(
        h5("Data table"),
        dataTableOutput("dt"),
        tags$head(HTML(format.func))
    ) 
)
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)
    ))
})
library(shiny)

format.func <- "
<script type='text/javascript'>
var _ajax_url = null;

function format ( d ) {
    // `d` is the original data object for the row
    return '<table cellpadding=\"5\" cellspacing=\"0\" border=\"0\" style=\"padding-left:50px;\">'+
        '<tr>'+
            '<td>Full name:</td>'+
            '<td>'+d.Name+'</td>'+
        '</tr>'+
        '<tr>'+
            '<td>Extension number:</td>'+
            '<td>'+d.Extension+'</td>'+
        '</tr>'+
    '</table>';
}

$(document).ready(function() {
  $('#ajax_req_url').on('change', function() { _ajax_url = $(this).val()});
})
</script>
"

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))
    ) 
)
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)
    ))
})