Is there a way to use indexes with inputIds?

Hello,

I am trying to create the following in R Shiny:

  1. Allow user to search a database using certain filters
  2. After clicking the button “Update,” the server returns n search results
  3. Use renderUI() to create n actionButtons with inputId = btnResult[i] (where i goes from 1 to n), one actionButton for each search result.
  4. Include observeEvent(input$btnResult[i]) that changes the reactive value of myMap to i when clicked.
  5. renderPlotly to plot a map based on the value of myMap().
    I have completed steps 1, 2, and 5, but am having difficulty with steps 3 and 4.
    My issue is that to my understanding, input values cannot be written in the format input$btnResult[i], and you also cannot write a single generalized observeEvent() function that for any potential value of i, can listen for input$btnResult[i] to be clicked and on being clicked set myMap(i). Instead, I think I have to write n copies of observeEvent() as shown in the example code below. This isn’t that bad for 4 results, but if the user might return 50 results, it feels clunky to repeat an observeEvent() block for each potential value of i. An example of what does work is shown below for n = 4.
library(shiny)
library(plotly)
data(iris)
iris

# example data (as a list because my database API call returns results in lists)
dfMap<- list()
dfMap[[1]] <- c(1,2,3)
dfMap[[2]] <- c(2,4,6)
dfMap[[3]] <- c(3,6,9)
dfMap[[4]] <- c(4,8,12)


ui <- fluidPage(
    uiOutput("matchHistory"),
    plotlyOutput("pltMap")
)



server <- function(input, output) {
    
    myMap <- reactiveVal(0)
    
    output$matchHistory <- renderUI ({
        
        lstMatches <- list()
       
        ## now add all the matches to the list one by one
        for (i in 1:4) {
            lstMatches[[i]] <- tags$div(class="match",
                                        ### button indexed as btnMatch1, btnMatch2, etc. 
                                        actionButton(paste0("btnMatch", i), "Review")
            )
        }
        lstMatches #return match list
    })
    
    
    observeEvent(input$btnMatch1, {
        myMap(1)
    })
    
    observeEvent(input$btnMatch2, {
        myMap(2)
    })
    
    observeEvent(input$btnMatch3, {
        myMap(3)
    })
    
    observeEvent(input$btnMatch4, {
        myMap(4)
    })
    
    output$pltMap <- renderPlotly({
        if (myMap() > 0) {
            plot_ly(x = 1:3, y = dfMap[[myMap()]], type = "bar") 
        }
    })
}

shinyApp(ui = ui, server = server)

However, for larger values of n, this becomes cumbersome because I need to repeat observeEvent() for every single button. I am looking for something like

actionButton(paste0("btnMatch[", i, "]"), "Review")

and one single generalized observeEvent() a la

observeEvent(input$btnMatch[i], {
        myMap(i)
    })

But this kind of formatting is not allowed as HTML object IDs cannot be indexed or in an array. Is there some sort of function within R Shiny that would allow me to circumvent this?

I hope I explained my problem clearly and thank you for your time.

Hi,

Welcome to the RStudio community!

This seems to be an issue that could be solved with making a shiny module
https://shiny.rstudio.com/articles/modules.html

Hope this helps,
PJ

Hi,

I would do something like this

library(shiny)
library(plotly)
data(iris)
iris

dfMap<- list()
dfMap[[1]] <- c(1,2,3)
dfMap[[2]] <- c(2,4,6)
dfMap[[3]] <- c(3,6,9)
dfMap[[4]] <- c(4,8,12)


ui <- fluidPage(
  uiOutput("matchHistory"),
  plotlyOutput("pltMap")
)

server <- function(input, output) {
  myMap <- reactiveValues(value = integer(),
                          state = sapply(1:length(dfMap), FUN = function(i) 0L))
  
  output$matchHistory <- renderUI ({
    lapply(1:length(dfMap), FUN = function(i) {
      tags$div(class="match",
               actionButton(paste0("btnMatch", i), "Review"))
    })
  })
  
  observeEvent(lapply(1:length(dfMap), FUN = function(i) input[[paste0("btnMatch",i)]]), ignoreInit = TRUE, {
    new_state = sapply(1:length(dfMap), FUN = function(i) input[[paste0("btnMatch",i)]])
    myMap$value = which(myMap$state != new_state)
    myMap$state = new_state
  })
  
  output$pltMap <- renderPlotly({
    if(length(myMap$value) != 0) {
      plot_ly(x = 1:3, y = dfMap[[myMap$value]], type = "bar") 
    }
  })
}


shinyApp(ui = ui, server = server)
1 Like

This did exactly what I needed. Thanks for your time , there is a lot for me to learn from in your code.

I am not familiar with modules. I spent some time this evening reading through example cases of how to use them, and I'm not 100% sure that I need them anymore as I think @gitdemont 's code will accomplish the same thing. Thanks for the suggestion!

This topic was automatically closed 54 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.