R Shiny - multi-page editable DataTable jumps to row #1 after an edit
Asked Answered
Q

2

9

I am developing a Shiny app using R 3.3.1, Shiny v. 1.2.0 and v. DT 0.5. One of the elements is an editable data table that spans multiple pages. After I make an edit the row in focus jumps to row #1 which kind of ruins the user experience.

Here are the specific steps to reproduce this using the snippet below:

  1. Load the app
  2. Switch to page 2 of the data table
  3. Edit row 3, column 2: change Duh to Blue and press Tab
  4. Watch the current row jump to page 1 row 1. This would be easier to see if the were many more rows per page.

What I get in step 4 is not the desirable behavior. I want the data table to keep the focus on the same row I just edited.

I am open to using custom JS logic to make this work.

Seemingly related question - DataTable doesn't remember paginated page after edit but I do not know how to bridge from R to JS in this particular example.

 R.version.string
# "R version 3.3.1 (2016-06-21)"

library(shiny)  # v. 1.2.0
library(DT)  # v. 0.5

page_length <- 2 # 5 elements should span 3 pages

hardcoded_df <- read.table(text = "Fruit Color
                                   Apple Red
                                   Plum Purple
                                   Blueberry Duh
                                   Orange Carrot
                                   Crocodile Green",
                           header = TRUE,
                           stringsAsFactors = FALSE)

ui <- fluidPage(
   DT::dataTableOutput('x1')
)

server <- function(input, output) {
  x = reactiveValues(df = hardcoded_df)

   output$x1 = renderDT(DT::datatable(x$df, options = list(pageLength = page_length), selection = 'none', editable = TRUE))

   proxy = dataTableProxy('x1')

   observeEvent(input$x1_cell_edit, {
     info = input$x1_cell_edit
     str(info)

     # str(input$x1_state)
     i = info$row
     j = info$col
     v = info$value

     # Without this line the table does not change but with it it jumps to row 1 after an edit.
     x$df[i, j] <- isolate(DT::coerceValue(v, x$df[i, j]))

     # Now we need to scroll to row i somehow ... clearly this does not work. Help!
     selectPage(proxy, ceiling(i / page_length))
     # selectRow(proxy, i)
   })
}

# Run the application 
shinyApp(ui = ui, server = server)
Quadruple answered 18/3, 2019 at 6:8 Comment(0)
E
7

In this situation DT::replaceData with resetPaging = FALSE should work fine as shown here. However, defining x as a reactiveValues() cause some problems which I solved using isolate

 server <- function(input, output, session) {
    x = reactiveValues(df = hardcoded_df)
    output$x1 = renderDT(DT::datatable(isolate(x$df), 
                options = list(pageLength = page_length), selection = 'none', editable = TRUE))

    proxy = dataTableProxy('x1')

    data = reactiveValues()
    observeEvent(input$x1_cell_edit, {
      info = input$x1_cell_edit
      str(info)
      # str(input$x1_state)
      i = info$row
      j = info$col
      v = info$value

      # Without this line the table does not change but with it it jumps to row 1 after an edit.
      x$df[i, j] <- isolate(DT::coerceValue(v, x$df[i, j]))
      DT::replaceData(proxy, x$df, resetPaging = FALSE)  # important
      # Now we need to scroll to row i somehow ... clearly this does not work. Help!
      #selectPage(proxy, ceiling(i / page_length))
      # selectRow(proxy, i)
    })
  }
Estaminet answered 18/3, 2019 at 8:11 Comment(4)
Thank you. Did you make other changes apart from adding the line DT::replaceData(proxy, x$df, resetPaging = FALSE) # important? I can't spot any others visually. I ask because my production code is very different from this toy example, so I can't just copy and paste the entire server function.Quadruple
@Quadruple you're welcome. I did three modifications, 1- Comment selectPage(proxy, ceiling(i / page_length)). 2- Add DT::replaceData(proxy, x$df, resetPaging = FALSE). 3- Add isolate(x$df) in renderDT.Estaminet
Cool, I confirmed that this fully solves my problem - both the page and the row now remain in focus, so I marked this answer as accepted.Quadruple
@A.Suliman Notice that the "3. add isolate(x$df)" above refers the isolate inside DT::datatable on third row. Maybe it's just me but I first mistakenly isolated the row just before "DT::replaceData" row i.e. isolate(DT::coerceValue(v, x$df[i, j])).Lour
E
0

This is my proposal:

server <- function(input, output) {
  x = reactiveValues(df = hardcoded_df)

  output$x1 = renderDT(DT::datatable(x$df, options = list(pageLength = page_length), selection = 'none', editable = TRUE))

  observeEvent(input$x1_cell_edit, {
    info = input$x1_cell_edit
    str(info)

    # str(input$x1_state)
    i = info$row
    j = info$col
    v = info$value

    # Without this line the table does not change but with it it jumps to row 1 after an edit.
    proxy = dataTableProxy('x1')
    newdf <-  x$df
    newdf[i,j] <- coerceValue(v, newdf[i, j])
    print(newdf)
    replaceData(proxy, newdf,resetPaging = F)

    # selectRow(proxy, i)
  })
}

# Run the application 
shinyApp(ui = ui, server = server)

I am not sure if it is the cleanest way but it is the best I have been able to do.

Eglanteen answered 18/3, 2019 at 7:41 Comment(1)
Thanks. Apparently this line needs to change as ell or it will not work. See the answer above. output$x1 = renderDT(DT::datatable(x$df ...Quadruple

© 2022 - 2024 — McMap. All rights reserved.