Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Update row(s) of a Shiny DataTable while maintaining position

Tags:

r

dt

shiny

I am creating a Shiny app that displays data.frame information at the top of the screen and specific variable stats at the bottom. The user can navigate the data.frame columns by interacting with a DT::datatable object.

When a user clicks on a variable, detailed information is presented that can be edited. I would like this information to be updated and reflected in the datatable. My problem is that when I update the table, it is rendered and shown starting at the very beginning. How can I preserve the page and row selection of the datatable after making edits?

Here is a minimal working example that shows the mtcars dataset in a DT::datatable. I have some controls that update fields. Notice that datatable is re-rendered back to the first page.

library(shiny)

runApp(shinyApp(

  ui = fluidPage(
    title = "minimal-working-example",
    fluidRow(
      column(3, inputPanel(
        selectInput("field", "Field", choices = names(mtcars)),
        numericInput("value", "Value", 0),
        actionButton("submit", "Submit")
      )),

      column(9,
        DT::dataTableOutput("table")
      )
    )
  ),

  server = function(input, output) {

    v <- reactiveValues(mtcars=mtcars)

    observeEvent(input$submit, {
      v$mtcars[input$field] <- input$value
    })

    output$table <- DT::renderDataTable({
      DT::datatable(
        v$mtcars,
        selection = "single",
        options = list(pageLength = 5))
    })
  }
))

Session Info:

Session info --------------------------
 setting  value                       
 version  R version 3.3.0 (2016-05-03)
 system   x86_64, mingw32             
 ui       RStudio (0.99.902)          
 language (EN)                        
 collate  English_United States.1252  
 tz       America/Chicago             
 date     2016-07-11                  

Packages -------------------------------
 package     * version     date       source                        
 DT            0.1.45      2016-02-09 Github (rstudio/DT@a63e9ac)   
 shiny       * 0.13.0.9000 2016-02-08 Github (rstudio/shiny@e871934)
like image 385
Zelazny7 Avatar asked Jul 11 '16 20:07

Zelazny7


1 Answers

This can be done from inside R without getting into the structure of the datatable through JS or something like that.

We utilize the various table state information we get from the DT package to render the new updated datatable like the one before. Everything we use is discribed in this DT documentation.

Item one: Selection. You can pre-select rows by adding selected = ... inside the selection argument of the datatable. This can be combined with the variable input$table_rows_selected to save the previously selected row and pre-select that exact row on re-rendering.

Item two: Page. The datatable package has an option displayStart that specifies which row should be shown first when rendering the table. Documentation here. So, if you have 5 rows per page, displayStart = 9 would start the display on page 3. (JavaScript arrays start at 0, so always subtract 1.) This can be combined with input$table_rows_current which is a vector of currently visible row numbers. If we store the first entry (minus 1), we know where to start the display.

Full code example below:

library(shiny)

runApp(shinyApp(

  ui = fluidPage(
    title = "minimal-working-example",
    fluidRow(
      column(3, inputPanel(
        selectInput("field", "Field", choices = names(mtcars)),
        numericInput("value", "Value", 0),
        actionButton("submit", "Submit")
      )),

      column(9,
        DT::dataTableOutput("table")
      )
    )
  ),

  server = function(input, output) {

    v <- reactiveValues(mtcars=mtcars)
    previousSelection <- NULL
    previousPage <- NULL

    observeEvent(input$submit, {
      previousSelection <<- input$table_rows_selected
      previousPage <<- input$table_rows_current[1] - 1

      v$mtcars[input$field] <- input$value
    })

    output$table <- DT::renderDataTable({
      DT::datatable(
        v$mtcars,
        selection = list(mode = "single", target = "row", selected = previousSelection),
        options = list(pageLength = 5, displayStart = previousPage))
    })
  }
))
like image 140
K. Rohde Avatar answered Nov 15 '22 15:11

K. Rohde