user8229029
user8229029

Reputation: 1162

What is correct method for observing multiple events in Shiny?

I need a data table to update each time a different date range, # of decimals to display, or a common vs all set of variables is chosen by the user. The code below works, but I don't think it's "correct". The three variables I need the code to respond to (input$descr_daterange, input$descr_radio, and input$descr_decimals) are located within the renderDataTable function. That doesn't seem like the right way to do this.

    observeEvent(input$descr_daterange, {
            descr_date_inds <- reactiveValues()
            descr_date_inds$begin_ind <- min(which(substr(inputData$qcdata$LDT,1,10) == input$descr_daterange[1]))
            descr_date_inds$end_ind <- max(which(substr(inputData$qcdata$LDT,1,10) == input$descr_daterange[2]))
            
            output$data_descr <- renderDataTable({
                    description <- descr(inputData$qcdata[descr_date_inds$begin_ind:descr_date_inds$end_ind,],transpose = TRUE, stats = input$descr_radio)
                    description <- description[-which(row.names(description) == 'RecNum'),]
                    
                    if ('N.Valid' %in% colnames(description) & 'Pct.Valid' %in% colnames(description)){
                            description <- description[,-which(colnames(description) == 'N.Valid' | colnames(description) == 'Pct.Valid')]}
                    
                    description <- round2(description[,2:ncol(description)],input$descr_decimals)
                    return(description)
                    autoWidth = TRUE
                    options=list(pageLength = 50)
            })
    })

I've seen code examples that list the variables like this:

  observeEvent(c(input$descr_daterange, input$descr_decimals, input$descr_radio),  
{...})

However, I get warnings that only the first of the list is being used. How do I code this to get it to to work correctly?

Upvotes: 0

Views: 541

Answers (1)

r2evans
r2evans

Reputation: 160407

You were close: put all reactive inputs to cause a trigger within braces.

Try this:

observeEvent({
  input$descr_daterange
  input$descr_decimals
  input$descr_radio
}, {
  descr_date_inds <- reactiveValues()
  descr_date_inds$begin_ind <- min(which(substr(inputData$qcdata$LDT,1,10) == input$descr_daterange[1]))
  descr_date_inds$end_ind <- max(which(substr(inputData$qcdata$LDT,1,10) == input$descr_daterange[2]))
  
  output$data_descr <- renderDataTable({
    description <- descr(inputData$qcdata[descr_date_inds$begin_ind:descr_date_inds$end_ind,],transpose = TRUE, stats = input$descr_radio)
    description <- description[-which(row.names(description) == 'RecNum'),]
    
    if ('N.Valid' %in% colnames(description) & 'Pct.Valid' %in% colnames(description)){
      description <- description[,-which(colnames(description) == 'N.Valid' | colnames(description) == 'Pct.Valid')]}
    
    description <- round2(description[,2:ncol(description)],input$descr_decimals)
    return(description)
    autoWidth = TRUE
    options=list(pageLength = 50)
  })
})

By-the-ways:

  1. reactiveValues() tend to belong outside of reactive blocks, and used/referenced within them. I don't know the rest of your app nor how you intend to use them, so it might be that you don't need it at all (and are just using its list/env functionality), in which case you are carrying unneeded overhead by doing it this way.

  2. You should never use & or | within an if conditional unless it is wrapped in any or all or some aggregating function. if requires its conditional to be exactly length 1, whereas &/| are vectorized logical ands/ors, meaning they can be length 0 or more.

    Even if both sides of the & are length-1, there is still a reason: && short-circuits, & does not.

    ### short-circuiting
    TRUE || stop("hello")
    # [1] TRUE
    
    ### not
    TRUE | stop("hello")
    # Error: hello
    

    Lastly, it's a bit declarative, in that by forcing yourself to use &&, you have to think about the length of the conditional. By using &, you're implying (to whomever is reading/using your code) that you will accept a result of length other than 1 ... which doesn't always work cleanly for if.

Upvotes: 3

Related Questions