5

I am trying to make a datatable that has two layers of nesting. The first one is used for grouping rows (https://github.com/rstudio/shiny-examples/issues/9#issuecomment-295018270) and the second should open a modal (R shinyBS popup window). I can get this to work individually but the second layer of nesting is creating problems. As soon as there is a second nesting the data in the table no longer show up in the collapsed group.

So there is at least one issue with what I have done so far and that is how to get it to display correctly when there are multiple nestings. After that I am not sure the modal would currently work. I wonder if the ids won't conflict the way it is done now.

Any hints are appreciated.

# Libraries ---------------------------------------------------------------
library(DT)
library(shiny)
library(shinyBS)
library(shinyjs)

library(tibble)
library(dplyr)
library(tidyr)
library(purrr)


# Funs --------------------------------------------------------------------

# Callback for nested rows
nest_table_callback <- function(nested_columns, not_nested_columns){

    not_nested_columns_str <- not_nested_columns %>% paste(collapse="] + '_' + d[") %>% paste0("d[",.,"]")

    paste0("
            table.column(1).nodes().to$().css({cursor: 'pointer'});

            // Format data object (the nested table) into another table
            var format = function(d) {
              if(d != null){ 
                var result = ('<table id=\"child_' + ",not_nested_columns_str," + '\">').replace('.','_') + '<thead><tr>'
                for (var col in d[",nested_columns,"]){
                  result += '<th>' + col + '</th>'
                }
                result += '</tr></thead></table>'
                return result
              }else{
                return '';
              }
            }

            var format_datatable = function(d) {
              var dataset = [];
              for (i = 0; i < + d[",nested_columns,"]['model'].length; i++) {
                var datarow = [];
                for (var col in d[",nested_columns,"]){
                  datarow.push(d[",nested_columns,"][col][i])
                }
                dataset.push(datarow)
              }
              var subtable = $(('table#child_' + ",not_nested_columns_str,").replace('.','_')).DataTable({
                'data': dataset,
                'autoWidth': true, 
                'deferRender': true, 
                'info': false, 
                'lengthChange': false, 
                'ordering': true, 
                'paging': false, 
                'scrollX': false, 
                'scrollY': false, 
                'searching': false 
              });
            };

            table.on('click', 'td.details-control', function() {
              var td = $(this), row = table.row(td.closest('tr'));
              if (row.child.isShown()) {
                row.child.hide();
                td.html('&oplus;');
              } else {
                row.child(format(row.data())).show();
                td.html('&CircleMinus;');
                format_datatable(row.data())
              }
            });
           "
          )

}



# This function will create the buttons for the datatable, they will be unique
shinyInput <- function(FUN, len, id, ...) {inputs <- character(len)
                                           for (i in seq_len(len)) {
                                             inputs[i] <- as.character(FUN(paste0(id, i), ...))}
                                           inputs
}


add_view_col <- . %>% {bind_cols(.,View = shinyInput(actionButton, nrow(.),'button_', label = "View", onclick = 'Shiny.onInputChange(\"select_button\",  this.id)' ))}



# Example nested data -----------------------------------------------------


collapse_col <- "to_nest"
modal_col <- "to_modal"

# nested data
X <- mtcars %>% 
                        rownames_to_column("model") %>% 
                        as_data_frame %>% 
                        select(mpg, cyl, model, everything()) %>%
                        nest(-mpg, -cyl, .key=!!modal_col) %>% #-#-#-#-#-#- WORKS IF THIS IS REMOVED #-#-#-#-#-#
                        nest(-mpg, .key=!!collapse_col)







data <- X %>% 
         {bind_cols(data_frame(' ' = rep('&oplus;',nrow(.))),.)} %>%
         mutate(!!collapse_col := map(!!rlang::sym(collapse_col), add_view_col))



collapse_col_idx <- which(collapse_col == colnames(data))

not_collapse_col_idx <- which(!(seq_along(data) %in% c(1,collapse_col_idx)))

callback <- nest_table_callback(collapse_col_idx, not_collapse_col_idx)




ui <- fluidPage( DT::dataTableOutput('my_table'),
                 uiOutput("popup")
                 )


server <- function(input, output, session) {

  my_data <- reactive(data)  


  output$my_table <- DT::renderDataTable(my_data(),
                                         options = list(columnDefs = list(
                                                                            list(visible = FALSE, targets = c(0,collapse_col_idx) ), # Hide row numbers and nested columns
                                                                            list(orderable = FALSE, className = 'details-control', targets = 1) # turn first column into control column
                                                                          )
                                                        ),
                                         server = FALSE,
                                         escape = -c(2),
                                         callback = JS(callback),
                                         selection = "none"
                                         )


  # Here I created a reactive to save which row was clicked which can be stored for further analysis
  SelectedRow <- eventReactive(input$select_button,
                                as.numeric(strsplit(input$select_button, "_")[[1]][2])
                              )

  # This is needed so that the button is clicked once for modal to show, a bug reported here
  # https://github.com/ebailey78/shinyBS/issues/57
  observeEvent(input$select_button, {
                                      toggleModal(session, "modalExample", "open")
                                    }
               )

  DataRow <- eventReactive(input$select_button,
                           my_data()[[collapse_col_idx]][[SelectedRow()]]
                           )

  output$popup <- renderUI({
                              bsModal("modalExample",
                                      paste0("Data for Row Number: ", SelectedRow()),
                                      "",
                                      size = "large",
                                      column(12, DT::renderDataTable(DataRow()))
                                    )
                          })

}

shinyApp(ui, server)

enter image description here

Jan Stanstrup
  • 1,152
  • 11
  • 28
  • I almost solved this this with the help of davlee1972 on the github issue. Only thing left is that the same button cannot be clicked twice. This is because the row doesn't change so the modal is never triggered. Asked here https://stackoverflow.com/questions/38575655/shiny-modules-namespace-outside-of-ui-for-javascript-links and here https://stackoverflow.com/questions/45739303/r-shiny-handle-action-buttons-in-data-table; that is the original modal example and a question with the same solution. I will report back when that is hopefully solved. – Jan Stanstrup Oct 06 '17 at 13:44

0 Answers0