1

I am using renderUI to create a "DisplayName" input and I have a reactive function called "ReactiveTest" that uses input$DisplayName. If you run the code below you will see that the "DisplayName" drop down list calls the NameAndID() which in turn calls GetDisplayName() in the global.r file. That call populates the "DisplayName" dropdown list.

BUT when ReactiveTest() tries to access input$DisplayName it says that input$DisplayName is NULL then there seems to be a second call to ReactiveTest() and THEN it sees the input$DisplayName

so when you run the code you will see the timing looks like this:

[1] "Department dropdown list is being returned now"
[1] "Department dropdown list is being returned now"

Listening on http://155.0.0.1:555
[1] "DISPLAY NAME dropdown list is being returned now"
[1] "Now ReactiveTest() tries to access input$DisplayName"
[1] "ReactiveTest() says input$DisplayName Name is NULL" --> why is it NULL? if 2 lines above the dropdown was populated???
[1] "Now ReactiveTest() tries to access input$DisplayName"--> Where is this coming from? Shouldn't there only be  1 call to ReactiveTest()
[1] "ReactiveTest() says input$DisplayName Name is Bob" 

I have 3 questions:

(1) Why does ReactiveTest() not see the input$DisplayName after the DisplayName dropdown is called?

(2) Why is there a second call to ReactiveTest()?

(3) How can I make ReactiveTest() see whats in the "DisplayName" dropdown the first call? In my real code I have to test for is.null(input$DisplayName)== TRUE and then not run some code but isn't there a better way?

Here is my code which you can run:

library(shiny)

runApp(list(
  ui = basicPage(
    sidebarPanel(
      selectInput("Department", "Select a department", 
                  choices = as.character(GetDepartments()$Department), 
                  selected = as.character(GetDepartments()$Department[1])),
      uiOutput("DisplayName")
    ),
    mainPanel(textOutput("Text") )
  ),
  server = function(input, output, session) {

    NameAndID<- reactive({
      GetDisplayName(input$Department)
    })

    output$DisplayName<-renderUI({
      Department <- input$Department
      selectInput("DisplayName", 'DisplayName:', choices = as.character(NameAndID()$DisplayName), selected =as.character(NameAndID()$DisplayName[1] ))
    })

    ReactiveTest<- reactive({
      print("Now ReactiveTest() tries to access input$DisplayName")
      if(is.null(input$DisplayName) == TRUE)
      {
        print("ReactiveTest() says input$DisplayName Name is NULL")
        return("NULL")
      }else
      {
        print(paste0("ReactiveTest() says input$DisplayName Name is ", input$DisplayName))
        return(input$DisplayName)
      }
    })

    output$Text <- renderText({
      #myData <- GetDisplayName(input$Department)
      #code <- as.character(myData[myData$DisplayName == input$DisplayName,2])

      return(ReactiveTest())       
    })

  }
))

global.r

GetDepartments<- function(){
  df<- data.frame(Department= c("Dept A", "Dept B"), id = c(1,2))
  print("Department dropdown list is being returned now")
  return(df)
}

GetDisplayName<- function(Dept){

  if(Dept == "Dept A")
  {
    df<- data.frame(DisplayName= c("Bob", "Fred"), id = c(4,6))
    print("DISPLAY NAME dropdown list is being returned now")
    return(df)
  }else
  {
    df<- data.frame(DisplayName= c("George", "Mary"), id = c(10,20))
    print("DISPLAY NAME dropdown list is being returned now")
    return(df)
  }

}
user3022875
  • 8,598
  • 26
  • 103
  • 167

1 Answers1

1

Your questions are essentially all asking the same thing, why is the function being called twice?

As I mentioned in your previous question, reactive expressions will only run again if they realize something has changed in the input. When you initialize a shiny app with a reactive ui, the values will not be initially loaded (i.e. null values). This has come up before with other shiny questions, such as this one on google groups and a similar one on SO, and the general consensus is that you should have some sort of check in your code (would love to be proven wrong). So your instinct to add a is.null check is correct. This is therefore a nice opportunity to use the validate function. Although this is initialization problem is somewhat annoying, the time taken during initialization should be insignificant.

Here is an example using validate. This however makes it very easy to provider a user friendly error message if somehow a null value is passed and anything passed the validate is not run. More straightforward, in the reactive expression, the initialization only prints and then leaves the function. This type of validation is recommended and documentation can be found here

runApp(list(
    ui = basicPage(
        sidebarPanel(
            selectInput("Department", "Select a department", 
                        choices = as.character(GetDepartments()$Department), 
                        selected = as.character(GetDepartments()$Department[1])),
            uiOutput("DisplayName")
        ),
        mainPanel(textOutput("Text") )
    ),
    server = function(input, output, session) {

        NameAndID<- reactive({
            GetDisplayName(input$Department)
        })

        output$DisplayName<-renderUI({
            Department <- input$Department
            selectInput("DisplayName", 'DisplayName:', choices = as.character(NameAndID()$DisplayName), selected =as.character(NameAndID()$DisplayName[1] ))
        })

        ReactiveTest<- reactive({
            print("Now ReactiveTest() tries to access input$DisplayName")
            validate(
                need(!is.null(input$DisplayName), "ReactiveTest() says input$DisplayName Name is NULL")
            )
            print(paste0("ReactiveTest() says input$DisplayName Name is ", input$DisplayName))
            return(input$DisplayName)
        })

        output$Text <- renderText({            
            return(ReactiveTest())       
        })  
    }
))
Community
  • 1
  • 1
cdeterman
  • 19,630
  • 7
  • 76
  • 100