Search code examples
rshinydtselectinput

How to save edits made in DT while using SelectInput in correct position


When using editable DataTable (package DT) where input is chosen by SelectInput the edits made isn't saved where it should be.

Choosing a specific variable shows for example rows 50-60 from datatable in first rows. Editing them saves the edits on the first rows instead of in rows 50-60.

In example below you can choose variable versicolor and delete setosa. Then edit Petal.Length to random number. Edit should be saved in row 51 but instead it is saved in row 1.

I am thinking about a workaround based on an index-column so the edits are saved in row number (indexrow). But I am not sure how to do that.

#Packages
library(shiny)
library(shinyalert)
library(shinydashboard)
library(leaflet)
library(leaflet.extras)
library(DT)

#data
iris = iris

#Shiny-app (ui)
header = dashboardHeader(title = "SelectInput DataTable example")

sidebar = dashboardSidebar(selectInput("species", "Choose species:  ",    choices = iris$Species, selected = "setosa",  multiple = TRUE))

body = dashboardBody(fluidRow(dataTableOutput("table")))

ui = dashboardPage(skin = "red", header, sidebar, body)

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


  output$table = DT::renderDataTable(iris[iris$Species %in% input$species,], editable = TRUE)

  proxy = dataTableProxy('table')

  observeEvent(input$table_cell_edit, {
    info = input$table_cell_edit
    str(info)
    i = info$row
    j = info$col
    v = info$value
    iris[i, j] <<- DT::coerceValue(v, iris[i, j])
    replaceData(proxy, iris, resetPaging = FALSE)  # important
  })
}

shinyApp(ui = ui, server = server)

Solution

  • Try this:

    #Packages
    library(shiny)
    library(shinydashboard)
    library(DT)
    
    #data
    iris = iris
    
    #Shiny-app (ui)
    header = dashboardHeader(title = "SelectInput DataTable example")
    
    sidebar = dashboardSidebar(selectInput("species", "Choose species: ", 
                                           choices = iris$Species, selected = "setosa",  multiple = TRUE))
    
    body = dashboardBody(fluidRow(DT::dataTableOutput("table")))
    
    ui = dashboardPage(skin = "red", header, sidebar, body)
    
    # Javascript 
    js <- function(rows){
      c(
        "function(settings){",
        "  var table = settings.oInstance.api();",
        sprintf("  var indices = [%s];", paste0(rows-1, collapse = ",")),
        "  table.rows(indices).remove().draw();",
        "}"
      )
    }
    
    #Server
    server <- function(input, output, session) {
    
      dat <- reactiveVal(iris)
    
      Rows <- reactive({
        which(iris$Species %in% input$species)
      })
    
      output$table = DT::renderDataTable({
        rows <- setdiff(1:nrow(iris), Rows())
        datatable(
          dat(), 
          editable = TRUE,
          options = list(
            initComplete = JS(js(rows))
          )
        )
      }, server = FALSE)
    
      observeEvent(input$table_cell_edit, {
        info = input$table_cell_edit
        info$row = Rows()[info$row+1] - 1
        dat(editData(dat(), info))
      })
    }
    
    shinyApp(ui = ui, server = server)