1

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)

1 Answer 1

1

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)
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.