具有Reactive功能的动态UI - 时序如何工作?

时间:2015-02-03 19:29:25

标签: r shiny

我正在使用renderUI创建一个“DisplayName”输入,我有一个名为“ReactiveTest”的反应函数,它使用输入$ DisplayName。如果运行下面的代码,您将看到“DisplayName”下拉列表调用NameAndID(),而NameAndID()又调用global.r文件中的GetDisplayName()。该调用填充了“DisplayName”下拉列表。

但是当ReactiveTest()尝试访问输入$ DisplayName时,它表示输入$ DisplayName为NULL然后似乎第二次调用ReactiveTest()然后它看到输入$ DisplayName

因此,当您运行代码时,您会看到时间如下:

[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" 

我有3个问题:

(1)为什么ReactiveTest()在调用DisplayName下拉列表后看不到输入$ DisplayName?

(2)为什么第二次调用ReactiveTest()?

(3)如何让ReactiveTest()在第一次调用的“DisplayName”下拉列表中看到什么?在我的真实代码中,我必须测试is.null(输入$ DisplayName)== TRUE然后不运行一些代码,但是没有更好的方法吗?

以下是我可以运行的代码:

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)
  }

}

1 个答案:

答案 0 :(得分:1)

你的问题基本上都是问同样的事情,为什么函数会被调用两次?

正如我在上一个问题中提到的,如果反应式表达式意识到输入中的某些内容发生了变化,它们将只会再次运行。使用反应式ui初始化闪亮的应用程序时,不会初始加载值(即空值)。之前提出过其他一些有待提问的问题,例如Google群组中的this oneSO上类似的问题,而且普遍的共识是您应该对代码进行某种检查(我会喜欢被证明是错误的)。因此,您添加is.null检查的直觉是正确的。因此,这是使用validate函数的好机会。虽然这是初始化问题有点烦人,但初始化过程中所花费的时间应该是微不足道的。

以下是使用validate的示例。但是,如果以某种方式传递空值并且未运行传递validate的任何内容,则可以很容易地提供用户友好的错误消息。更直接的是,在反应式表达式中,初始化仅打印然后离开函数。建议使用此类验证,并且可以找到文档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())       
        })  
    }
))