17

I am running into an issue because observe is being called first before the UI loads.

Here is my ui.R

  sidebarPanel(
    selectInput("Desk", "Desk:" ,  as.matrix(getDesksUI())),
    uiOutput("choose_Product"), #this is dynamically created UI
    uiOutput("choose_File1"), #this is dynamically created UI
    uiOutput("choose_Term1"), #this is dynamically created UI  ....

Here is my Server.R

shinyServer(function(input, output,session) {

  #this is dynamic UI
  output$choose_Product <- renderUI({ 
    selectInput("Product", "Product:", as.list(getProductUI(input$Desk)))
  })

   #this is dynamic UI
  output$choose_File1 <- renderUI({
    selectInput("File1", "File 1:", as.list(getFileUI(input$Desk, input$Product)))
  })

  #this is dynamic UI and I want it to run before the Observe function so the call
  # to getTerm1UI(input$Desk, input$Product, input$File1) has non-null parameters
  output$choose_Term1 <- renderUI({
    print("Rendering UI for TERM")
    print(paste(input$Desk," ", input$Product, " ", input$File1,sep=""))
    selectInput("Term1", "Term:", getTerm1UI(input$Desk, input$Product, input$File1))
  })

This is my observe function and it runs before the input$Product and input$File1 are populated so I get an error because they are both NULL. But I need to use the input from the UI.

   observe({ 
    print("in observe")
    print(input$Product)
    max_plots<-length(getTerm2UI(input$Desk, input$Product, input$File1))
    #max_plots<-5
            # Call renderPlot for each one. Plots are only actually generated when they
            # are visible on the web page.
            for (i in 1:max_plots )   {
              # Need local so that each item gets its own number. Without it, the value
              # of i in the renderPlot() will be the same across all instances, because
              # of when the expression is evaluated.
              local({
               my_i <- i
                plotname <- paste("plot", my_i, sep="")
                output[[plotname]] <- renderPlot({
                  plot(1:my_i, 1:my_i,
                       xlim = c(1, max_plots ),
                       ylim = c(1, max_plots ),
                        main = paste("1:", my_i, ". n is ", input$n, sep = "") )
                })
              })
            }##### End FoR Loop
 },priority = -1000)

Any idea how to get the input$Product and input$File1 to be populated BEFORE observe runs?

Thank you.

user3022875
  • 8,598
  • 26
  • 103
  • 167
  • 1
    `observe` has a `priority = 0` argument. An observer with a given priority level will always execute sooner than all observers with a lower priority level. You can make priority negative if you want it to execute later. This may help. – jdharrison Apr 03 '14 at 18:53
  • I set the priority to -100 and nothing different happens – user3022875 Apr 03 '14 at 19:50

4 Answers4

14

EDIT: Scroll down to TClavelle's answer for a better solution. While this answer has the most upvotes, I wrote it when Shiny had fewer features than it does today.

The simplest way is to add an is.null(input$Product) check at the top of each observe, to prevent it from running before the inputs it uses are initialized.

If you don't want your observers to do the null-check each time they're run, you can also use the suspended = TRUE argument when registering them to prevent them from running; then write a separate observer that performs the check, and when it finds that all inputs are non-null, calls resume() on the suspended observers and suspends itself.

Fadeway
  • 549
  • 5
  • 18
12

You can use req() to "require" an input before a reactive expression executes, as per the Shiny documentation here: https://shiny.rstudio.com/articles/req.html and the function documentation here: https://shiny.rstudio.com/reference/shiny/latest/req.html

e.g.

observeEvent({
  req(input$Product)
  req(input$File1)
  # ...
})
TClavelle
  • 578
  • 4
  • 12
  • 1
    This is the cleaner, more canonical way of doing it compared to the `is.null(.)` suggested in another answer. – Devin Mar 08 '19 at 16:43
  • 1
    This looks like another example of a SO top answer solving an issue, yet not in the *right* way. Using [req](https://shiny.rstudio.com/articles/req.html) for this, *a failure not only stops the current calculation [...] but also any callers on the call stack.* You can give all inputs as comma separated arguments as in `req(input$Product, input$File1)` as well. – Trusky Aug 09 '21 at 14:44
  • 1
    @Trusky good point, edited my answer to point people toward this one. – Fadeway Sep 06 '21 at 21:01
11

You need to use the Shiny Event Handler and use observeEvent instead of observe. It seems to be about the only way to get rid of the "Unhandled error" message caused by NULL values on app startup. This is so because unlike observe the event handler ignores NULL values by default.

So your observe function could end up looking something like this (no need for priorities, or resume/suspended etc!)

observeEvent(input$Product, ({

  max_plots<-length(getTerm2UI(input$Desk, input$Product, input$File1))
  ... (etc)      

  })# end of the function to be executed whenever input$Product changes
 )

I could not copy paste your example code easily to make it run, so I'm not entirely sure what your full observe function would look like.

FvD
  • 3,697
  • 1
  • 35
  • 53
1

We'd need an MRE to provide a working answer, but, assuming you need input$Product and input$File1, but do not want to take a dependency on them, only on input$Desk, you could:

observe({ 

  product <- isolate(input$Product)
  file1 <- isolate(input$File1)

  print("in observe")
  print(product)
  max_plots<-length(getTerm2UI(input$Desk, product, file1))
  for (i in 1:max_plots )   {
    # ...
  }
})

this is probably effectively equivalent to an observeEvent(input$Desk, ....), but might offer more flexibility.

Aurèle
  • 12,545
  • 1
  • 31
  • 49