In the Shiny App below, I have added a few buttons to my DT and with every click I would like to +/- 1 to the number column in the DT. This works, but when a button is clicked twice the second time it is not working. It works again, when first a button on another row is pressed.
I believe that the input$mydata_number_minus
somehow needs to be reset in the observeEvent() code block. I have done various attempts, but none of them successful. I hope one of you can lead me to the magic trick.
library(shiny)
library(DT)
# Define UI
ui <- fluidPage(
# Create DT with in-cell inputSelect dropdown
DTOutput("mytable"),
# Output to print reactive data frame
verbatimTextOutput("verbotentext")
)
# Define server
server <- function(input, output, session) {
# Define sample data
mydata <- data.frame(
Name = c("John", "Mary", "Bob"),
number = 5,
Age = c(25, 30, 35),
Gender = c("Male", "Female", "Male"),
Color = c("Red", "Green", "Blue")
)
rv_mydata <- reactiveVal(mydata)
# Render DT
output$mytable <- renderDT({
mydata <- rv_mydata()
# Add in-cell inputSelect dropdown to "Color" column
mydata$Color <- paste0(
"<select class='form-control' onchange='",
"var index = $(this).closest(\"tr\").index();",
"Shiny.setInputValue(\"mydata_color\",",
"{",
" row_index: index,",
" new_value: $(this).val()",
"});'>",
"<option value='Red'", ifelse(mydata$Color == "Red", " selected", ""), ">Red</option>",
"<option value='Green'", ifelse(mydata$Color == "Green", " selected", ""), ">Green</option>",
"<option value='Blue'", ifelse(mydata$Color == "Blue", " selected", ""), ">Blue</option>",
"</select>"
)
mydata$number_actions <- paste0(
"<div style='display:flex;justify-content:center;'>",
"<button onclick='",
"var index = $(this).closest(\"tr\").index();",
"Shiny.setInputValue(\"mydata_number_minus\",",
"{",
" row_index: index",
"});'>",
"<</button>",
"<div style='padding:0px 5px;width:20px;text-align:center;'>",
mydata$Number,
"</div>",
"<button onclick='",
"var index = $(this).closest(\"tr\").index();",
"Shiny.setInputValue(\"mydata_number_plus\",",
"{",
" row_index: index",
"});'>",
">></button>",
"</div>"
)
datatable(mydata, escape = FALSE, selection = "none", options = list(dom = 't', paging = FALSE, ordering = FALSE))
})
# Define reactive action triggered by in-cell inputSelect dropdown
observeEvent(input$mydata_color, {
# input$mydata_color %>% print()
row_index <- input$mydata_color$row_index + 1
new_value <- input$mydata_color$new_value
mydata <- rv_mydata()
mydata[row_index, "Color"] <- new_value
rv_mydata(mydata)
})
observeEvent(input$mydata_number_minus, {
row_index <- input$mydata_number_minus$row_index + 1
mydata <- rv_mydata()
mydata[row_index, "number"] <- mydata[row_index, "number"] - 1
rv_mydata(mydata)
input$mydata_number_minus %>% print()
## !! Need code to reset input$mydata_number_minus so that I can press it multiple times to execute this block
input$mydata_number_minus %>% print()
})
observeEvent(input$mydata_number_plus, {
row_index <- input$mydata_number_plus$row_index + 1
mydata <- rv_mydata()
mydata[row_index, "number"] <- mydata[row_index, "number"] + 1
rv_mydata(mydata)
})
# Print reactive data frame
output$verbotentext <- renderPrint({
rv_mydata()
})
}
# Run app
shinyApp(ui, server)
We can add nonce:Math.random()
to the shiny.setInputValue
so that the input is different with every click and will therefore be invalidated (and updated).
library(shiny)
library(DT)
# Define UI
ui <- fluidPage(
# Create DT with in-cell inputSelect dropdown
DTOutput("mytable"),
# Output to print reactive data frame
verbatimTextOutput("verbotentext")
)
# Define server
server <- function(input, output, session) {
# Define sample data
mydata <- data.frame(
Name = c("John", "Mary", "Bob"),
number = 5,
Age = c(25, 30, 35),
Gender = c("Male", "Female", "Male"),
Color = c("Red", "Green", "Blue")
)
rv_mydata <- reactiveVal(mydata)
# Render DT
output$mytable <- renderDT({
mydata <- rv_mydata()
# Add in-cell inputSelect dropdown to "Color" column
mydata$Color <- paste0(
"<select class='form-control' onchange='",
"var index = $(this).closest(\"tr\").index();",
"Shiny.setInputValue(\"mydata_color\",",
"{",
" row_index: index,",
" new_value: $(this).val()",
"});'>",
"<option value='Red'", ifelse(mydata$Color == "Red", " selected", ""), ">Red</option>",
"<option value='Green'", ifelse(mydata$Color == "Green", " selected", ""), ">Green</option>",
"<option value='Blue'", ifelse(mydata$Color == "Blue", " selected", ""), ">Blue</option>",
"</select>"
)
mydata$number_actions <- paste0(
"<div style='display:flex;justify-content:center;'>",
"<button onclick='",
"var index = $(this).closest(\"tr\").index();",
"Shiny.setInputValue(\"mydata_number_minus\",",
"{",
" row_index: index,",
" nonce: Math.random()",
"});'>",
"<</button>",
"<div style='padding:0px 5px;width:20px;text-align:center;'>",
mydata$Number,
"</div>",
"<button onclick='",
"var index = $(this).closest(\"tr\").index();",
"Shiny.setInputValue(\"mydata_number_plus\",",
"{",
" row_index: index,",
" nonce: Math.random()",
"});'>",
">></button>",
"</div>"
)
datatable(mydata,
escape = FALSE,
selection = "none",
options = list(dom = 't', paging = FALSE, ordering = FALSE)
)
})
# Define reactive action triggered by in-cell inputSelect dropdown
observeEvent(input$mydata_color, {
# input$mydata_color %>% print()
row_index <- input$mydata_color$row_index + 1
new_value <- input$mydata_color$new_value
mydata <- rv_mydata()
mydata[row_index, "Color"] <- new_value
rv_mydata(mydata)
})
observeEvent(input$mydata_number_minus, {
row_index <- input$mydata_number_minus$row_index + 1
mydata <- rv_mydata()
mydata[row_index, "number"] <- mydata[row_index, "number"] - 1
rv_mydata(mydata)
input$mydata_number_minus %>% print()
## !! Need code to reset input$mydata_number_minus so that I can press it multiple times to execute this block
input$mydata_number_minus %>% print()
})
observeEvent(input$mydata_number_plus, {
row_index <- input$mydata_number_plus$row_index + 1
mydata <- rv_mydata()
mydata[row_index, "number"] <- mydata[row_index, "number"] + 1
rv_mydata(mydata)
})
# Print reactive data frame
output$verbotentext <- renderPrint({
rv_mydata()
})
}
# Run app
shinyApp(ui, server)