combination of wide spread JS DT script for radio buttons and script for Child/parent table do not work together

The two wildely used examples by @yihui and @stla (Nested Datatable Within Another Datatable · Issue #619 · rstudio/DT · GitHub), do not seem to work when implemented together. I am building a shiny app where the user selects some values using radio buttons in a DT. . I am not sure if I incorrectly bound the JS or that the JS somehow contradicts each other? Either way, I am not able to access the user input anymore (just a null returns if I look at the structure of the input$xxx).

library(shiny)
library(DT)
library(shinyWidgets)
library(tidyverse)


shinyApp(
  ui = fluidPage(
    title = 'Radio button and a dropdown manue ',
    sliderInput("n_rows_table", "Number of rows:",
                min = 0, max = 10,
                value = 5),
    actionBttn(
      inputId = "btnCancel",
      label = "Make tables",
      size = "sm",
      color = "warning"
    ),
    p("THIS EXAMPLE DOES NOT WORK!"),
    DT::dataTableOutput("datatable"),
    verbatimTextOutput('sel'),
    p("THIS SIMPLER EXAMPLE DOES WORK!"),
    DT::dataTableOutput("datatable2"),
    verbatimTextOutput('sel2'),
    
  ),
  
  server = function(input, output, session) {
    
    
    
    # ----- Create a table based on the number of rows from the slider
    # ----- and create it when the user clicks the button
    data_for_table <-  eventReactive(
      input$btnCancel,
      {
        tibble(
          let_rowid = letters[1:input$n_rows_table],
          val_1 = round(runif(input$n_rows_table, 0, 10), 1),
          val_2 = round(rnorm(input$n_rows_table), 2),
          val_3 = round(rnorm(input$n_rows_table), 2),
          val_4 = letters[1:input$n_rows_table],
          Yes   = "Yes",
          No    = "No",
          Maybe = "Maybe",
          result = NA
        )  %>%
          mutate(oplus = "&oplus;") %>%
          relocate(oplus) %>%
          mutate(
            Yes =  sprintf('<input type="radio" name="%s" value="%s"/>', let_rowid , Yes),
            No =  sprintf('<input type="radio" name="%s" value="%s"/>', let_rowid , No),
            Maybe =  sprintf('<input type="radio" name="%s" value="%s"/>', let_rowid ,  Maybe)
          ) %>%
          nest(datalist = c(val_3, val_4)) %>%
          mutate(datalist = map(datalist, as.list)) %>%
          mutate(datalist = map(datalist, list))
        
      })
    
    # ----- Render the table
    # ----- The table renders ok.
    output$datatable <- DT::renderDT({
      parentRows <- which(data_for_table()[,1] != "")
      
      # ------ This JS is neede to make the child/parent dropdown
      callback <- JS(
        sprintf("var parentRows = [%s];", toString(parentRows-1)),
        sprintf("var j0 = %d;", 0),
        "var nrows = table.rows().count();",
        "for(let i = 0; i < nrows; ++i){",
        "  var $cell = table.cell(i,j0).nodes().to$();",
        "  if(parentRows.indexOf(i) > -1){",
        "    $cell.css({cursor: 'pointer'});",
        "  }else{",
        "    $cell.removeClass('details-control');",
        "  }",
        "}",
        "",
        "// --- make the table header of the nested table --- //",
        "var formatHeader = function(d, childId){",
        "  if(d !== null){",
        "    var html = ",
        "      '<table class=\"display compact hover\" ' + ",
        "      'style=\"padding-left: 30px;\" id=\"' + childId + ",
        "      '\"><thead><tr>';",
        "    var data = d[d.length-1] || d.datalist;",
        "    for(let key in data[0]){",
        "      html += '<th>' + key + '</th>';",
        "    }",
        "    html += '</tr></thead></table>'",
        "    return html;",
        "  } else {",
        "    return '';",
        "  }",
        "};",
        "",
        "// --- row callback to style rows of child tables --- //",
        "var rowCallback = function(row, dat, displayNum, index){",
        "  if($(row).hasClass('odd')){",
        "    $(row).css('background-color', 'papayawhip');",
        "    $(row).hover(function(){",
        "      $(this).css('background-color', '#E6FF99');",
        "    }, function(){",
        "      $(this).css('background-color', 'papayawhip');",
        "    });",
        "  } else {",
        "    $(row).css('background-color', 'lemonchiffon');",
        "    $(row).hover(function(){",
        "      $(this).css('background-color', '#DDFF75');",
        "    }, function(){",
        "      $(this).css('background-color', 'lemonchiffon');",
        "    });",
        "  }",
        "};",
        "",
        "// --- header callback to style header of child tables --- //",
        "var headerCallback = function(thead, data, start, end, display){",
        "  $('th', thead).css({",
        "    'border-top': '3px solid indigo',",
        "    'color': 'indigo',",
        "    'background-color': '#fadadd'",
        "  });",
        "};",
        "",
        "// --- make the datatable --- //",
        "var formatDatatable = function(d, childId){",
        "  var data = d[d.length-1] || d.datalist;",
        "  var colNames = Object.keys(data[0]);",
        "  var columns = colNames.map(function(x){",
        "    return {data: x.replace(/\\./g, '\\\\\\.'), title: x};",
        "  });",
        "  var id = 'table#' + childId;",
        "  if(colNames.indexOf('datalist') === -1){",
        "    var subtable = $(id).DataTable({",
        "      'data': data,",
        "      'columns': columns,",
        "      'autoWidth': true,",
        "      'deferRender': true,",
        "      'info': false,",
        "      'lengthChange': false,",
        "      'ordering': data.length > 1,",
        "      'order': [],",
        "      'paging': false,",
        "      'scrollX': false,",
        "      'scrollY': false,",
        "      'searching': false,",
        "      'sortClasses': false,",
        "      'rowCallback': rowCallback,",
        "      'headerCallback': headerCallback,",
        "      'columnDefs': [{targets: '_all', className: 'dt-center'}]",
        "    });",
        "  } else {",
        "    var subtable = $(id).DataTable({",
        "      'data': data,",
        "      'columns': columns,",
        "      'autoWidth': true,",
        "      'deferRender': true,",
        "      'info': false,",
        "      'lengthChange': false,",
        "      'ordering': data.length > 1,",
        "      'order': [],",
        "      'paging': false,",
        "      'scrollX': false,",
        "      'scrollY': false,",
        "      'searching': false,",
        "      'sortClasses': false,",
        "      'rowCallback': rowCallback,",
        "      'headerCallback': headerCallback,",
        "      'columnDefs': [",
        "        {targets: -1, visible: false},",
        "        {targets: 0, orderable: false, className: 'details-control'},",
        "        {targets: '_all', className: 'dt-center'}",
        "      ]",
        "    }).column(0).nodes().to$().css({cursor: 'pointer'});",
        "  }",
        "};",
        "",
        "// --- display the child table on click --- //",
        "// array to store id's of already created child tables",
        "var children = [];",
        "table.on('click', 'td.details-control', function(){",
        "  var tbl = $(this).closest('table'),",
        "      tblId = tbl.attr('id'),",
        "      td = $(this),",
        "      row = $(tbl).DataTable().row(td.closest('tr')),",
        "      rowIdx = row.index();",
        "  if(row.child.isShown()){",
        "    row.child.hide();",
        "    td.html('&oplus;');",
        "  } else {",
        "    var childId = tblId + '-child-' + rowIdx;",
        "    if(children.indexOf(childId) === -1){",
        "      // this child has not been created yet",
        "      children.push(childId);",
        "      row.child(formatHeader(row.data(), childId)).show();",
        "      td.html('&CircleMinus;');",
        "      formatDatatable(row.data(), childId, rowIdx);",
        "    }else{",
        "      // this child has already been created",
        "      row.child(true);",
        "      td.html('&CircleMinus;');",
        "    }",
        "  }",
        "}); ",
        "table.rows().every(function(i, tab, row) {",
        "                    var $this = $(this.node());",
        "                    $this.attr('id', this.data()[0]);", 
        "                    $this.addClass('shiny-input-radiogroup');",
        "  });",
        "           Shiny.unbindAll(table.table().node());",
        "           Shiny.bindAll(table.table().node());")
      
      
      datatable(
        data_for_table(),
        escape = F,
        rownames = F,
        callback = callback,
        options = list(
          dom = 't',
          paging = FALSE,
          ordering = FALSE,
          paging = FALSE,
          searching = FALSE,
          columnDefs = list(
            list(
              visible = FALSE, 
              targets = c(ncol(data_for_table())-1+0) # do not show certain ID variables, we do not need 
            ),
            list(
              orderable = FALSE, 
              className = "details-control", 
              targets = 0
            ),
            list(
              className = "dt-left", 
              targets = "_all"
            )
          )
        )
        
      )
    },
    server = F)
    
    
    list_results <- reactive({
      list_values <- list()
      for (i in unique(data_for_table()$let_rowid)) {
        list_values[[i]] <- paste0(i, ": ", input[[i]])
        
      }
      list_values
    })
    
    output$sel = renderPrint({
      list_results()
    })
    
    
    ####################################
    ## this simpler version does work ##
    ####################################
    
    data_for_table2 <-  eventReactive(
      input$btnCancel,
      {
        tibble(
          let_rowid = letters[11:(10+input$n_rows_table)],
          val_1 = round(runif(input$n_rows_table, 0, 10), 1),
          val_2 = round(rnorm(input$n_rows_table), 2),
          val_3 = round(rnorm(input$n_rows_table), 2),
          val_4 = letters[1:input$n_rows_table],
          Yes   = "Yes",
          No    = "No",
          Maybe = "Maybe",
          result = NA
        )  %>%
          mutate(
            Yes =  sprintf('<input type="radio" name="%s" value="%s"/>', let_rowid , Yes),
            No =  sprintf('<input type="radio" name="%s" value="%s"/>', let_rowid , No),
            Maybe =  sprintf('<input type="radio" name="%s" value="%s"/>', let_rowid ,  Maybe)
          )       })
    
    
    output$datatable2 <- DT::renderDT({
      
      # ---- only difference here is he lack of a drop down.
      callback <- JS("table.rows().every(function(i, tab, row) {
                    var $this = $(this.node());
                    $this.attr('id', this.data()[0]);
                    $this.addClass('shiny-input-radiogroup');
  });
                    Shiny.unbindAll(table.table().node());
                    Shiny.bindAll(table.table().node());")
      
      
      datatable(
        data_for_table2(),
        escape = F,
        rownames = F,
        callback = callback,
        options = list(
          dom = 't',
          paging = FALSE,
          ordering = FALSE,
          paging = FALSE,
          searching = FALSE,
          columnDefs = list(
            # list(
            #   visible = FALSE, 
            #   targets = c(ncol(data_for_table2())-1+0) # do not show certain ID variables, we do not need 
            # ),
            list(
              orderable = FALSE, 
              className = "details-control", 
              targets = 0
            ),
            list(
              className = "dt-left", 
              targets = "_all"
            )
          )
        )
        
      )
    },
    server = F)
    
    list_results2 <- reactive({
      list_values <- list()
      for (i in unique(data_for_table2()$let_rowid)) {
        list_values[[i]] <- paste0(i, ": ", input[[i]])
        
      }
      list_values
    })
    
    output$sel2 = renderPrint({
      list_results2()
    })
  }
)

This topic was automatically closed 21 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.