rshinydt

Column headers move when on a different tab using replaceData()?


I've identified an issue with DT (stable: 0.33, dev: 0.33.1) for R Shiny (1.9.1) that might be resolvable through some Javascript.

Specifically, when using multiple tabs (see reprex below) if using DT::replaceData() to update a DT table with scroller on a different tabset from where a button is clicked, the first render will be correct:

correct header orientation on first click

But the second time the button is clicked, the headers "squish" together on the left-hand side:

incorrect header orientation on second click

This only seems to be an issue when any form of scrolling is enabled (which in this case, is the mandatory functionality) regardless of whether the scroller extension (and pagination) is used or not. Not using the scroller does not result in this behavior.

Reprex:

library(shiny)
library(DT)

# doesn't work
 ui = fluidPage(

   tabsetPanel(
     tabPanel("button",
              actionButton("change", "Change")
              ),
     tabPanel("table",
              DT::dataTableOutput("table"))
   )

 )


# works
# ui = fluidPage(
#   
#   tabsetPanel(
#     tabPanel("button and table in one tab",
#              tags$br(),
#              actionButton("change", "Change"),
#              tags$br(),
#              DT::dataTableOutput("table"))
#   )
#   
# )

server = function(input, output, session) {
  
  cars_plus = mtcars
  cars_plus$new =  sample(100, 1)
  
  rvs = reactiveValues(
    cars = cars_plus
  )
  
  
  
  table_proxy = DT::dataTableProxy("table")
  
  output$table = DT::renderDataTable(server = T, {
    
    DT::datatable(isolate(rvs$cars),
                  rownames = FALSE,
                  extensions = c('Buttons'),
                  options=list(
                    buttons = list(
                      list(extend = 'colvis', text = 'Choose visible columns')
                    ),
                    dom = '<"top"B>rt<"bottom">i',
                    scrollY = '200',
                    paging = F
                  )
    )
    
  })
  
  observeEvent(input$change,{
    
    dat = rvs$cars
    
    changer = sample(100, 1)
    
    dat[3, "new"] = changer
    
    rvs$cars = dat
    
    DT::replaceData(table_proxy, dat, rownames = F, resetPaging = F, clearSelection = "none")
    
  })
}

shinyApp(ui, server)

Solution

  • There are some issues with scrollY. You can avoid this by adding an event handler on shown.bs.tab which resets the width of the dataTable to 100% and calls columns.adjust():

    $(document).ready(function() {
      $('a[data-toggle="tab"]').on( 'shown.bs.tab', function (e) {
        $($.fn.dataTable.tables( true ) ).css('width', '100%');
        $($.fn.dataTable.tables( true ) ).DataTable().columns.adjust().draw();
      });
    });
    

    enter image description here

    library(shiny)
    library(DT)
    
    ui = fluidPage(
      tags$head(tags$script(HTML(
        "$(document).ready(function() {
           $('a[data-toggle=\"tab\"]').on( 'shown.bs.tab', function (e) {
             $($.fn.dataTable.tables( true ) ).css('width', '100%');
             $($.fn.dataTable.tables( true ) ).DataTable().columns.adjust().draw();
           });
         });"
      ))),
      tabsetPanel(
        tabPanel("button",
                 actionButton("change", "Change")
        ),
        tabPanel("table",
                 DT::dataTableOutput("table"))
      )
    )
    
    server = function(input, output, session) {
      
      cars_plus = mtcars
      cars_plus$new =  sample(100, 1)
      
      rvs = reactiveValues(
        cars = cars_plus
      )
      
      table_proxy = DT::dataTableProxy("table")
      
      output$table = DT::renderDataTable(server = T, {
        
        DT::datatable(isolate(rvs$cars),
                      rownames = FALSE,
                      extensions = c('Buttons'),
                      options=list(
                        buttons = list(
                          list(extend = 'colvis', text = 'Choose visible columns')
                        ),
                        dom = '<"top"B>rt<"bottom">i',
                        scrollY = '200',
                        paging = F
                      )
        )
        
      })
      
      observeEvent(input$change,{
        
        dat = rvs$cars
        
        changer = sample(100, 1)
        
        dat[3, "new"] = changer
        
        rvs$cars = dat
        
        DT::replaceData(table_proxy, dat, rownames = F, resetPaging = F, clearSelection = "none")
        
      })
    }
    
    shinyApp(ui, server)