Dynamically create and manage observeEvents (annotation app)

Hello everyone, this is my first post on this forum :slight_smile:

I am having a problem with designing a shiny application - in summary, I would like my app to be a annotation tool - it downloads some textual data from DB. A user is presented with this data, and can add an annotation to the data - it then gets added to DB, and user is presented with status of addition (it could fail, so this outcome needs to be presented).

My biggest problem is that I don't know how to approach dynamically creating and assigning observeEvents, and then managing them. In case the user selected different set of filters, I have no idea on how to remove existing observeEvents - since they are dumped in server's body, and are not assigned to any value that could be removed/updated.

My app looks like this:


library(shiny)
library(dplyr)
library(tibble)
library(purrr)

dt <- tribble(
    ~ID,  ~Content, ~Note,
    'AB', 'Lorem ipsum', NA_character_,
    'BC', 'dolor sit amet', NA_character_, 
    'CD', 'consectetur adipiscing', NA_character_,
    'DE', 'sed do eiusmod', NA_character_,
    'EF', 'tempor incididunt', NA_character_,
    'FG', 'ut labore et dolore', NA_character_
)

# Simulate a 50% change of failure.
simulateInsert <- function() {
    ifelse(runif(1) < .5, TRUE, FALSE)
}

ui <- fluidPage(
    titlePanel("Dynamic Buttons"),

    sidebarLayout(
        sidebarPanel(
            sliderInput("buttons",
                        "Number of buttons:",
                        min = 3,
                        max = 6,
                        value = 4),
            uiOutput('dynamicButtons')
        ),

        mainPanel(
            uiOutput('mainUI')
        )
    )
)

server <- function(input, output) {

    comm <- reactiveValues(
                           data = NULL
    )
    
    # Simulate data download and filtering
    observeEvent(input$buttons,
                 {
                     comm$data <- dt[1:input$buttons, ]
                 })
    
    
    # Buttons and textInput creation part
    mainUI <- reactive({
        req(comm$data)
        ids <- isolate(comm$data$ID)
        print(ids)
        imap(ids, 
             function(x, y)
             {
                 output[[paste0('content', y)]] <- renderText(as.character(y))
                 output[[paste0('result', y)]] <- renderText('')
             fluidRow(
                 textOutput(paste0('content', y)),
                 textInput(paste0('txt', y), paste0('Text Input ', x)),
                 actionButton(paste0('but', y), paste0('Send', x)),
                 textOutput(paste0('result', y))
             )
             }
        )
    })
    output$mainUI <- renderUI({
        mainUI()
    })
    
    observeEvent(mainUI(),
        {
            req(mainUI())
            imap(comm$data,
                 function(x, y)
                     {
                     observeEvent(input[[paste0('but', y)]],
                                  {
                                      print('Executing')
                                      if(simulateInsert() == TRUE) {
                                          output[[paste0('result', y)]] <- 'Success'
                                      } else{
                                          output[[paste0('result', y)]] <- 'Failed'
                                      }
                                  })
                 })
        
            })
    
}

# Run the application 
shinyApp(ui = ui, server = server)

The last observe does not work as expected - it never executes.

I have also considered a possibility of using:

observeEvent(input[[grepl("but", names(input))]],
        {

            ## Do... something that hmmmm... ?
        }
    )

But does not work because of error below. Even if it worked I am not sure how shall I sort out which button has been pressed (probably using an intermediate reactiveValues object)

Warning: Error in checkName: Must use single string to index into reactivevalues
  62: stop
  61: checkName
  60: [[.reactivevalues
  58: observeEventExpr
   1: runApp

Is there a way to do what I want that I am missing? Any suggestion is very welcome!

There is a helpful example on Stack Overflow :

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.