I have one code in which I add and remove objects by using insertUI and removeUI as it can be seen below:
library(shiny)
# Define the UI
ui <- fluidPage(
actionButton("adder", "Add"),
tags$div(id = 'placeholder')
)
# Define the server code
server <- function(input, output) {
rv <- reactiveValues()
rv$counter <- 0
observeEvent(input$adder,{
rv$counter <- rv$counter + 1
add <- sprintf("%03d",rv$counter)
filterId <- paste0('adder_', add)
divId <- paste0('adder_div_', add)
elementFilterId <- paste0('adder_object_', add)
removeFilterId <- paste0('remover_', add)
insertUI(
selector = '#placeholder',
ui = tags$div(
id = divId,
actionButton(removeFilterId, label = "Remove filter", style = "float: right;"),
textInput(elementFilterId, label = "Introduce text", value = "")
)
)
# Observer that removes a filter
observeEvent(input[[removeFilterId]],{
rv$counter <- rv$counter - 1
removeUI(selector = paste0("#", divId))
})
})
}
# Return a Shiny app object
shinyApp(ui = ui, server = server, options = list(launch.browser = T))
The problem that I'm experiencing is that if I add one UI (click on Add), then remove it (click on Remove filter) and then add a new one (click on Add again), the first time I click on it, doesn't work.
I know that it's due to the fact that I'm using an ID I've used previously but, theoretically, I've completely removed it with the removeUI instruction.
What am I missing in here?
FYI there is a way to clear the Server of input IDS so you don't run into the same problem. I found the solution here.
https://roh.engineering/post/shiny-add-removing-modules-dynamically/
The article goes into a lot of detail, but you can use the following function to remove inputs from the server.
remove_shiny_inputs <- function(id, .input) {
invisible(
lapply(grep(id, names(.input), value = TRUE), function(i) {
.subset2(.input, "impl")$.values$remove(i)
})
)
}
Though I consider that there should be a more elegant way of solving this problem, I have found a workaround that solves it.
In summary, I've added to the ID of the object that I create a random string that I've created; thus, the id will never be repeated.
library(shiny)
generateRandomString <- function(n = 10, m = 10) {
elements <- c()
chars <- c(LETTERS, letters)
for(idx in 1:n) {
element <- c()
for(entry in 1:m) {
val <- sample(c("pair","odd"),1)
switch(val,
pair = {
# Add a letter
element <- c(element, sample(chars,1))
},
odd = {
# Add a number
element <- c(element, as.character(sample(0:9,1)))
}
)
}
elements <- c(elements, paste0(element,collapse=""))
}
elements
}
# Define the UI
ui <- fluidPage(
actionButton("adder", "Add"),
tags$div(id = 'placeholder')
)
# Define the server code
server <- function(input, output) {
rv <- reactiveValues()
rv$counter <- 0
observeEvent(input$adder,{
rv$counter <- rv$counter + 1
add <- sprintf("%03d",rv$counter)
prefix <- generateRandomString(1,20)
filterId <- paste0(prefix,'_adder_', add)
divId <- paste0(prefix,'_adder_div_', add)
elementFilterId <- paste0(prefix,'_adder_object_', add)
removeFilterId <- paste0(prefix,'_remover_', add)
insertUI(
selector = '#placeholder',
ui = tags$div(
id = divId,
actionButton(removeFilterId, label = "Remove filter", style = "float: right;"),
textInput(elementFilterId, label = "Introduce text", value = "")
)
)
# Observer that removes a filter
observeEvent(input[[removeFilterId]],{
rv$counter <- rv$counter - 1
removeUI(selector = paste0("#", divId))
})
})
}
# Return a Shiny app object
shinyApp(ui = ui, server = server, options = list(launch.browser = T))
There are better ways of creating such a random string but the point is that the issue is solved.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With