Search code examples
htmlrdatatablesshinydt

R Shiny selectedInput inside renderDataTable cells


I search for solution to put selectedInputs in renderDataTable cells. I found js solutions: https://datatables.net/examples/api/form.html , however I do not know how to implement this solution in shinyjs as renderDataTable object. I would be grateful for hints / ideas / solutions how to implement editable renderDataTable in shiny.


Solution

  • Very similar to this: adding a column with TRUE/FALSE and showing that as a checkbox

    library(shiny)
    library(DT) 
    runApp(list(
      ui = basicPage(
        h2('The mtcars data'),
        DT::dataTableOutput('mytable'),
        h2("Selected"),
        tableOutput("checked")
      ),
    
      server = function(input, output) {
        # helper function for making checkbox
        shinyInput = function(FUN, len, id, ...) { 
          inputs = character(len) 
          for (i in seq_len(len)) { 
            inputs[i] = as.character(FUN(paste0(id, i), label = NULL, ...)) 
          } 
          inputs 
        } 
        # datatable with checkbox
        output$mytable = DT::renderDataTable({
          data.frame(mtcars,Rating=shinyInput(selectInput,nrow(mtcars),"selecter_",
                                                choices=1:5, width="60px"))
        }, selection='none',server = FALSE, escape = FALSE, options = list( 
          paging=TRUE,
          preDrawCallback = JS('function() { 
    Shiny.unbindAll(this.api().table().node()); }'), 
          drawCallback = JS('function() { 
    Shiny.bindAll(this.api().table().node()); } ') 
        ) )
        # helper function for reading checkbox
        shinyValue = function(id, len) { 
          unlist(lapply(seq_len(len), function(i) { 
            value = input[[paste0(id, i)]] 
            if (is.null(value)) NA else value 
          })) 
        } 
        # output read checkboxes
        output$checked <- renderTable({
          data.frame(selected=shinyValue("selecter_",nrow(mtcars)))
        })
      }
    ))
    

    Note that if you rerender the table, the inputs won't work unless you add some extra code to unbind.

    edit:

    Let's say the data in the table is reactive so it changes, and the table rerenders. You will need to explicitely unbind as per @yihui here: https://groups.google.com/forum/#!msg/shiny-discuss/ZUMBGGl1sss/zfcG9c6MBAAJ

    So you need to add in the UI:

    tags$script(HTML("Shiny.addCustomMessageHandler('unbind-DT', function(id) {
              Shiny.unbindAll($('#'+id).find('table').DataTable().table().node());
            })"))
    

    And then in the Server you trigger the function whenever you rerender the datatable with:

    session$sendCustomMessage('unbind-DT', 'mytable')
    

    The colnames parameter is a vector of column names so when you specify a length one vector of FALSE it gives you a table with one column named FALSE. I am not sure of a straightforward way of removing column names from datatables. That would be a good SO question on its own.