Reputation: 23690
I have a Shiny app that allows user to edit record notes by clicking a button in the table. It works for first click, but immediately after you close the the edit dialogue (OK or cancel) the same button becomes innert. If you click a second button the first then becomes active again. Any idea how to fix this?
require(shiny)
require(DT)
require(tidyverse)
states_dat = tibble(id = state.abb, name = state.name, pop = state.x77[,1], note = NA_character_)
#------------------------------------------------
# Return the UI for a modal dialog with state update options
editModal = function(conv_id) {
ns = NS('state-update')
curr_mon_id <<- conv_id
conv = dplyr::filter(my_dt$df, id == conv_id$id)
modalDialog(
h3(paste0('Edit note ', conv_id)),
textInput(ns("edit_note"), "Note", value = conv$note, width='600px'),
footer = tagList(
modalButton("Cancel"),
actionButton(ns("ok"), "OK")
)
)
}
#------------------------------------------------
# Main screen server
dt_server = function(input, output, session) {
ns = session$ns
myValue = reactiveValues(check = '')
shinyInput = function(FUN, len, id, ns, ...) {
inputs = character(len)
for (i in seq_len(len)) inputs[i] = as.character(FUN(paste0(id, i), ...))
inputs
}
my_dt <<- reactiveValues(df = {
dat = states_dat
dat$` ` = shinyInput(actionButton, nrow(dat), 'button_', label = "Edit note",
onclick = glue::glue('Shiny.onInputChange("{ns("select_button")}", this.id)'))
dat
})
observeEvent(input$select_button, {
selectedRow = as.numeric(strsplit(input$select_button, "_")[[1]][2])
myValue$check <<- my_dt$df[selectedRow,1]
showModal(editModal(myValue$check))
})
output$states_table = DT::renderDT(filter = "top", {
return(my_dt$df)
}, escape = FALSE)
}
#------------------------------------------------
# Record Update server
mud_server = function(input, output, session) {
ns = session$ns
# When OK button is pressed, check if details have changed and if so update postgres db
observeEvent(input$ok, {
id = curr_mon_id$id[1]
if(!identical(input$edit_note, states_dat$note[states_dat$id == id])){
my_dt$df$note[my_dt$df$id == id] = input$edit_note
}
removeModal()
})
}
#------------------------------------------------
# Data table UI
dt_ui = function(id){
ns = NS(id)
tagList(
titlePanel( h3('State populations'), windowTitle = 'States-Pop'), hr(),
mainPanel(width=12, DT::DTOutput(ns("states_table"))
)
)
}
#------------------------------------------------
ui = fluidPage( dt_ui(id = "states-manager"))
server = function(input, output, session) {
callModule(module = dt_server , id = "states-manager")
callModule(module = mud_server , id = "state-update")
}
shinyApp(ui = ui, server = server)
Any other brief comments on how to improve this app would be very welcome. This is my first experience of Shiny modules so it be far from an optimal configuration. For instance note edits wipe any current table filters in place..
Upvotes: 2
Views: 64
Reputation: 20409
In your JS you set select_button
to the respective id
. A consequent click sets it to the same id
. Since the value does not change, the observer does not fire again. If you click another button in between, the value changes twice and it works again.
This behaviour is described here.
You can explicitely tell JS to treat a click as an event, rather than setting a value by adding {priority: "event"}
as described in the link.
Thus, the following snippet will do the trick:
my_dt <<- reactiveValues(df = {
dat = states_dat
dat$` ` = shinyInput(actionButton, nrow(dat), 'button_', label = "Edit note",
onclick = glue::glue('Shiny.setInputValue("{ns("select_button")}", this.id, {{priority: "event"}})'))
dat
})
Note. I used setInputValue
instead of onInputChange
as the latter is soft-deprecated in Shiny v 1.1.
Upvotes: 1