Bernard
Bernard

Reputation: 83

R Shiny reactive priority

I am building an app which I need to load some user settings into various input, including a dynamic selectInput where its values depends on another selectInput. I run into a problem where the dynamic selectInput that I want to update is immediately overwritten by other update events. The code below should better explain it

### Approach A ###
{
    ui <- fluidPage(
        selectInput("controlSelect", "Control", choices = c("A", "B","C"), multiple = FALSE),
        uiOutput("itemUI"),
        actionButton("button", "Button")
    )
    
    server <- function(input, output, session) {
        output$itemUI <- renderUI({
            print("rendering itemUI and itemSelect")
            itemToShow <- switch(input$controlSelect,
                                             "A" = c("a1", "a2", "a3"),
                                             "B" = c("b1", "b2", "b3"),
                                             "C" = c("c1", "c2", "c3")
            )

            result <- selectInput("itemSelect", "Items", choices = itemToShow, multiple = TRUE)

            return(result)
        })

        observeEvent(input$button, {
            print("observed button so run updates")
            updateSelectInput(session, "controlSelect", selected = "B")
            updateSelectInput(session, "itemSelect", selected = c("b1", "b3"))
        })
        
    }
    
    # Run the application
    shinyApp(ui = ui, server = server)
}

### Approach B ###
{
ui <- fluidPage(
    selectInput("controlSelect", "Control", choices = c("A", "B","C"), multiple = FALSE),
    selectInput("itemSelect", "items", choices = c("a1", "a2", "a3"), multiple = TRUE),
    actionButton("button", "Button")
)

server <- function(input, output, session) {

    itemsRV <- reactiveValues(items ="")
    
    observeEvent(itemsRV$items, {
        print("updating itemSelect with itemsRV")
        updateSelectInput(session, "itemSelect", selected = itemsRV$items)
    }, priority = 1)
    
    observeEvent(input$controlSelect, {
        
        itemToShow <- switch(input$controlSelect,
                                                 "A" = c("a1", "a2", "a3"),
                                                 "B" = c("b1", "b2", "b3"),
                                                 "C" = c("c1", "c2", "c3")
        )
        print("observed controlSelect so update itemSelect")
        updateSelectInput(session, "itemSelect", choices = itemToShow)
    }, priority = 10)
    
    observeEvent(input$button, {
        print("----------button clicked-----------------")
        
        print("updating itemsRV")
        itemsRV$items <- c("b1", "b3")
        
        print("updating controlSelect")
        updateSelectInput(session, "controlSelect", selected = "B")
        
    }, priority = 10)
    
}

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

Desired behavior:

Approach A is the first version I have written. It does not work but it simply demonstrates what I try to achieve. It does work though if “B” is already selected in Control before you click the button. Regardless it is not good enough. Approach B is a version which I attempt to mess with the priority options. It still does not work

I feel that the problem is not simply the priority of reactives, but the priority of event emission as well. Hopefully someone can help. Thanks!

Upvotes: 1

Views: 574

Answers (1)

MrFlick
MrFlick

Reputation: 206253

On way to get that behavior is a variation of approach B. But we use a different reactive value to set the current values if the Items. For example

ui <- fluidPage(
  selectInput("controlSelect", "Control", choices = c("A", "B","C"), multiple = FALSE),
  selectInput("itemSelect", "Items", choices=character(), multiple=TRUE),
  actionButton("button", "Button")
)

server <- function(input, output, session) {
  setItemValues <- reactiveVal(character())
  
  observeEvent(input$controlSelect, {
    itemToShow <- switch(input$controlSelect,
                         "A" = c("a1", "a2", "a3"),
                         "B" = c("b1", "b2", "b3"),
                         "C" = c("c1", "c2", "c3")
    )
    updateSelectInput(session, "itemSelect", 
                      choices = itemToShow, selected = setItemValues())
    setItemValues(character())
  })
  
  observeEvent(input$button, {
    print("observed button so run updates")
    updateSelectInput(session, "controlSelect", selected = "B")
    setItemValues(c("b1", "b3"))
  })
}

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

Since we relay on the input$controlSelect change to update the items in the drop down, we need to let those be updated first before we can choose the selected values. So on button click, we trigger the change of Control, but then just set a reactiveValue to indicate what we want the selected items to be when the control is updated. We then use those values on update and reset the reactive value.

Upvotes: 1

Related Questions