具有反应功能的动态 UI - 计时如何工作?
A dynamic UI with a Reactive function - How does timing work?
我正在使用 renderUI 创建一个 "DisplayName" 输入,我有一个名为 "ReactiveTest" 的反应函数,它使用 input$DisplayName。如果您 运行 下面的代码,您将看到 "DisplayName" 下拉列表调用 NameAndID(),后者又调用 global.r 文件中的 GetDisplayName()。该调用会填充 "DisplayName" 下拉列表。
但是,当 ReactiveTest() 尝试访问 input$DisplayName 时,它说 input$DisplayName 为 NULL,然后似乎对 ReactiveTest() 进行了第二次调用,然后它看到了 input$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) 为什么在调用 DisplayName 下拉菜单后 ReactiveTest() 看不到 input$DisplayName?
(2) 为什么要第二次调用 ReactiveTest()?
(3) 如何让 ReactiveTest() 在第一次调用时看到 "DisplayName" 下拉列表中的内容?在我的真实代码中,我必须测试 is.null(input$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)
}
}
你的问题基本上都是在问同样的事情,为什么这个函数被调用了两次?
正如我在你之前的问题中提到的,反应式表达式只有在意识到输入中发生了某些变化时才会再次 运行。当您使用反应式 ui 初始化闪亮的应用程序时,最初不会加载这些值(即空值)。之前已经提出了其他闪亮的问题,例如 this one on google groups and a similar one on SO,并且普遍的共识是您应该对您的代码进行某种检查(希望被证明是错误的)。所以你添加 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())
})
}
))
我正在使用 renderUI 创建一个 "DisplayName" 输入,我有一个名为 "ReactiveTest" 的反应函数,它使用 input$DisplayName。如果您 运行 下面的代码,您将看到 "DisplayName" 下拉列表调用 NameAndID(),后者又调用 global.r 文件中的 GetDisplayName()。该调用会填充 "DisplayName" 下拉列表。
但是,当 ReactiveTest() 尝试访问 input$DisplayName 时,它说 input$DisplayName 为 NULL,然后似乎对 ReactiveTest() 进行了第二次调用,然后它看到了 input$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) 为什么在调用 DisplayName 下拉菜单后 ReactiveTest() 看不到 input$DisplayName?
(2) 为什么要第二次调用 ReactiveTest()?
(3) 如何让 ReactiveTest() 在第一次调用时看到 "DisplayName" 下拉列表中的内容?在我的真实代码中,我必须测试 is.null(input$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)
}
}
你的问题基本上都是在问同样的事情,为什么这个函数被调用了两次?
正如我在你之前的问题中提到的,反应式表达式只有在意识到输入中发生了某些变化时才会再次 运行。当您使用反应式 ui 初始化闪亮的应用程序时,最初不会加载这些值(即空值)。之前已经提出了其他闪亮的问题,例如 this one on google groups and a similar one on SO,并且普遍的共识是您应该对您的代码进行某种检查(希望被证明是错误的)。所以你添加 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())
})
}
))