I am creating a Shiny app that displays a table and allows the user to edit existing rows or add new rows. The user can click any edit button in the table to open a modal dialog to edit the corresponding row, or can click a separate action button to add a new entry. The app uses modules. One module displays the table and handles the edit capability, and a second module handles the addition of new entries.
I've noticed strange behavior involving DT:datatable
and Shiny
. I'm using DT version 0.17, Shiny version 1.6.0, and R version 4.0.4.
In my example below, when filtering is enabled in the table by setting filter = "top"
in DT::datatable()
, the action button in the modal dialog for adding a new entry doesn't work. If filter = "none"
or if the table is hidden by commenting out, everything works. I've created a reproducible example using mtcars
to illustrate this behavior. In this example, the data are kept in a csv file and the two modules read and write to the file. In my real app, the data are in a database. In my real app, I've noticed other strange behavior too. Mainly, when the table filtering is enabled, the inputs in the modal dialog for adding a new entry become taller and the shinyvalidate
alerts don't appear.
I wonder if this is a bug in Shiny
or DT
or if there's a problem with my code. Any help figuring this out would be greatly appreciated.
The code below illustrates the issue. To make the code work as expected, comment out tableUI("Cars")
and callModule(tableServer, id = "Cars")
in the main app section or set filter = "none"
in DT::datatable()
.
# Libraries require(shiny) require(DT) # Create some data write.csv(data.frame(id = 1:5, car = rownames(mtcars)[1:5], mtcars[1:5, c("mpg", "cyl")]), file = "mtcars.csv", row.names = F) # Creates vector of edit buttons to insert in dataframe ButtonInput <- function(FUN, len, id, ...) { inputs <- character(len) for (i in seq_len(len)) { inputs[i] <- as.character(FUN(paste0(id, i), ...)) } inputs } # Updates data in csv from values in list update_data <- function(fileName, newValues, rowNum) { myData <- read.csv(fileName) for(colName in names(newValues)) { myData[rowNum, colName] <- newValues[[colName]] } write.csv(myData, file = fileName, row.names = F) } # Inserts data at top of csv file insert_data <- function(fileName, newValues) { myData <- read.csv(fileName) newData <- data.frame(car = newValues$car, mpg = newValues$mpg, cyl = newValues$cyl, id = NA) myData <- rbind(newData, myData) myData$id <- 1:nrow(myData) write.csv(myData, file = fileName, row.names = F) } # Table module ## tableUI <- function(id) { ns <- NS(id) fluidPage( DTOutput(ns("myTable")) ) } tableServer <- function(input, output, session) { # Get the row number for edit button that was clicked SelectedRow <- reactive({ req(input$select_button) splitButtonID <- strsplit(input$select_button, "_")[[1]] selectedRow <- as.numeric(splitButtonID[length(splitButtonID)]) return(selectedRow) }) # Checks for updates to mtcars.csv and returns it read_data <- reactivePoll( intervalMillis = 1000, session = session, checkFunc = function() read.csv("mtcars.csv"), valueFunc = function() read.csv("mtcars.csv") ) # Create dataframe with edit buttons in first column output$myTable <- renderDataTable({ outputDF <- data.frame( Edit = ButtonInput( FUN = actionButton, len = nrow(read_data()), id = session$ns("button_"), label = "Edit", onclick = paste0('Shiny.setInputValue(\"', session$ns("select_button"), '\", this.id, {priority: \"event\"})') ), read_data()[, c("car", "mpg", "cyl")] ) # When filter = "none", submission works. # When it's "top" or "bottom", submission doesn't work. DT::datatable( data = outputDF, filter = "top", rownames = F, escape = F ) }) # Show menu to edit entries when any edit button clicked observeEvent(input$select_button, { old_data <- read_data()[SelectedRow(), ] showModal(modalDialog( title = "Edit existing entry", easyClose = T, fluidPage( textInput(session$ns("edit_car"), label = "car", value = old_data$car), textInput(session$ns("edit_mpg"), label = "mpg", value = old_data$mpg), textInput(session$ns("edit_cyl"), label = "cyl", value = old_data$cyl), actionButton(session$ns("submit"), label = "Submit") ) )) }) # Update table when submit is clicked observeEvent(input$submit, { withProgress(message = "Updating data...", value = 0.5, { update_list <- as.list(read_data()[SelectedRow(), ]) update_list$car <- input$edit_car update_list$mpg <- input$edit_mpg update_list$cyl <- input$edit_cyl rowID <- update_list$id update_data("mtcars.csv", newValues = update_list, rowNum = rowID) }) showModal(modalDialog( title = "Thank you", "Your data have been updated." )) }) } ################################################################################ ## Insert module insertDataUI <- function(id) { ns <- NS(id) fluidPage( selectizeInput(ns("new_car"), label = "car", choices = c("bmw 325", "ford f150", "toyota camry", "porsche 911"), selected = NULL, options = list(create = T)), selectizeInput(ns("new_mpg"), label = "mpg", choices = 10:40, selected = NULL, options = list(create = T)), selectizeInput(ns("new_cyl"), label = "cyl", choices = c(4,6,8), selected = NULL, options = list(create = T)), actionButton(ns("submit"), label = "Submit") ) } insertDataServer <- function(input, output, session) { # Insert new data in first row of table when submit is clicked observeEvent(input$submit, { insert_list <- list( car = input$new_car, mpg = input$new_mpg, cyl = input$new_cyl ) withProgress(message = "Inserting data...", value = 0.5, { insert_data("mtcars.csv", newValues = insert_list) }) showModal(modalDialog( title = "Thank you", "Your data have been inserted." )) }) } ################################################################################ ## Main ## shinyApp( ui = fluidPage( br(), actionButton("insertButton", label = "Insert new entry"), br(), br(), tableUI("Cars") ), server = function(input, output, session) { callModule(tableServer, id = "Cars") observeEvent(input$insertButton, { callModule(insertDataServer, id = "insertMenu") showModal(modalDialog( title = "Insert new entry", easyClose = T, insertDataUI("insertMenu") )) }) } )
https://stackoverflow.com/questions/66700630/shiny-action-button-doesnt-work-inside-modal-dialog-when-filtering-is-enabled-i March 19, 2021 at 07:41AM
没有评论:
发表评论