Dynamically removing UI elements using insertUI and removeUI

#1

I have an app that inserts plots using the insertUI function. I am now trying to do the following:
When the user inserts a plot using the action button, another action button next to the plot should appear that has the functionality of removing that specific plot when clicked.

I believe that I am pretty close to the solution in the example: The buttons get inserted with the plots, however, only the first inserted button works (for all elements). How can I connect a button with the respective plots?

Any help would be highly appreciated

Here is a minimal expample:

library(shiny)
library(shinydashboard)

# Dummy data

a<-(letters)
b<-rnorm(length(letters), 4,2)
c<-rnorm(length(letters), 10,15)
d<-c(1:10,20:30,45:49)

data<-data.frame(a,b,c,d)
names(data)<-c("name","v1","v2","v3")

# UI

ui <- dashboardPage(
  
  
  dashboardHeader(),
  
  dashboardSidebar(
    actionButton("add", "Add"),
    radioButtons("add_elements","", c("Element1",	"Element2"))
  ),
  dashboardBody(
    fluidRow( tags$div(id="placeholder")
    )
))


# Server Logic

server <- function(input, output, session) {
  
 # Initialize empty vector
  inserted<- c()

  # Observer
  observeEvent(input$add, {
    id_add <- paste0(input$add, input$add_elements)

    insertUI(selector = '#placeholder', where = "afterEnd",
             ui= switch(input$add_elements,
                        'Element1'= plotOutput(id_add),
                        'Element2' = plotOutput(id_add))
    )
    
    output[[id_add]] <- 
      if (input$add_elements == "Element1") 
        
        renderPlot({
          plot(data[,1],data[,2])
        
          }) else if (input$add_elements == "Element2") 
          
          renderPlot({
            plot(data[,1],data[,4])
            })
    inserted <<- c(id_add,inserted)
    insertUI(
      selector = "#placeholder",
      where = "afterEnd",
      ui = tags$div(actionButton("remove_button", "Remove"))
      )})

  ## Remove Elements ###
  observeEvent(input$remove_button, {
    removeUI(
      selector = paste0('#', inserted[length(inserted)])
    )
    inserted <<- inserted[-length(inserted)]
  })
}

shinyApp(ui = ui, server = server)
0 Likes

#2

You can add logic to the "Remove" button to remove itself along with the plot.

observeEvent(input$remove_button, {
  removeUI(selector = paste0('#', inserted[length(inserted)]))
  inserted <<- inserted[-length(inserted)]
  removeUI(selector = "#remove_button")
})
0 Likes

#3

Thanks for the answer. To remove the action button with another selector is already helping.

However, the main problem is still not solved: The remove buttons do not react to the respective plot that was inserted and only the first inserted action button does the trick. If you insert several plots with the example code above, you will be able to reproduce the problem.

I believe that my issue is this line:

selector = paste0('#', inserted[length(inserted)])

I have no idea how I can match the selector "id" of the plot with the remove button so that the remove button only removes the specific plot. The example posted by @barbara has a very similar functionality, but I don't manage to implement it in my app:

https://beta.rstudioconnect.com/barbara/insertUI/

Your feedback would be highly appreciated.

0 Likes

#5

When adding your elements, add an observeEvent on the remove button right away. By also adding a unique ID to the remove button, we can differentiate the buttons added to the page.

I've added an updated server code below:

server <- function(input, output, session) {
  
  # Observer
  observeEvent(input$add, {
    # make ids for the plot, the remove button, and the element to remove
    id_add <- paste0(input$add, input$add_elements)
    remove_id <- paste0("remove_", id_add)
    ele_id <- paste0("ele_", id_add)

    # insert all of the ui at once inside a larger UI element (div) with a id value
    insertUI(
      selector = '#placeholder', 
      where = "afterEnd",
      ui= tags$div(
        id = ele_id,
        switch(input$add_elements,
          'Element1'= plotOutput(id_add),
          'Element2' = plotOutput(id_add)
        ),
        actionButton(remove_id, "Remove")
      )
    )

    if (input$add_elements == "Element1") {
      output[[id_add]] <- renderPlot({
        plot(data[,1],data[,2])
      })
    } else if (input$add_elements == "Element2") {
      output[[id_add]] <- renderPlot({
        plot(data[,1],data[,4])
      })
    }

    ## Remove Elements ###
    # when the specific remove button is clicked, remove the larger UI element containing the plot and button
    observeEvent(input[[remove_id]], {
      removeUI(
        selector = paste0("#", ele_id)
      )
    })

  })
  
}
1 Like

#6

Thank you very much for the solution, which is exactly what I was looking for. Sidenote: I have asked the same question in Stackoverflow which I meant to share here. However, the solution uses reactiveValues and list() in compination with renderUI.

0 Likes

#7

If your question's been answered (even if by you), would you mind choosing a solution? (See FAQ below for how).

Having questions checked as resolved makes it a bit easier to navigate the site visually and see which threads still need help.

Thanks

0 Likes

closed #8

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.

0 Likes