我有Shiny的问题,我不知道这是一个错误还是我错过了一些东西.我试图找到一个解决方案,但我似乎无法在网上找到这个问题的参考.
我有一个按下按钮时应该运行的功能,但它只在用户按下按钮后查看输出时才会运行.
这有点奇怪,所以我创建了一个最小的例子.我有两个标签.在第一个中,我选择一个数字(平均值),在第二个选项卡中,我绘制了该值的值.该按钮位于第一个选项卡中,但"create.plot"功能仅在转到tab2时才开始运行.为了清楚起见,我创建了这个'loading.div',它在函数运行时显示出来.
library(shiny)
library(shinyjs)
create.plot <- function(mean)
{
shinyjs::show('loading.div')
y <- rep(NA,10)
for(i in 1:10)
{
y[i] <- rnorm(1,mean=mean)
Sys.sleep(0.5)
}
shinyjs::hide('loading.div')
return(list(y=y, test='Now it runs from tab1'))
}
server <- function(input, output, session) {
run.function <- eventReactive(input$run,create.plot(input$mean))
output$plot.y <- renderPlot({
plot(run.function()[['y']])
})
output$test <- renderText({
run.function()[['test']]
})
}
ui <- fluidPage(shinyjs::useShinyjs(),
hidden(div(id='loading.div',h3('Loading..'))),
tabsetPanel(
tabPanel('tab1',
numericInput(inputId = 'mean',label='Mean', value=1),
actionButton(inputId = 'run', label='Run')
#,textOutput('test') ## Uncommenting this line, it works.
),
tabPanel('tab2',
plotOutput('plot.y'))
)
)
shinyApp(server=server,ui=ui)
Run Code Online (Sandbox Code Playgroud)
为了确保问题是用户在tab1时没有查看输出,我创建了一个测试字符串.如果取消注释该行,则问题是"修复":该功能在用户点击按钮后直接运行.但是,这只能起作用,因为他正在查看tab1中的输出.
这成为一个严重的问题,因为我有一个闪亮的仪表板,有很多标签.有带输入的选项卡和带输出的选项卡.我希望在单击按钮时运行该功能,但它仅在用户进入"输出"选项卡时运行.
这是一个已知的问题吗?有变通方法吗?
Ben*_*min 10
不可见的反应元素被视为"暂停",并且在取消暂停之前不会更新它们的值.默认情况下,直到它们在屏幕上呈现时才会发生这种情况.
即使不可见,也可以通过更改其挂起状态来设置要更新的元素.您可以使用该outputOptions功能执行此操作.
对于output$test元素,您需要运行
outputOptions(output, "test", suspendWhenHidden = FALSE)
Run Code Online (Sandbox Code Playgroud)
如果您想要更新多个元素,即使不可见,也可以使用
lapply(c("test1", "test2", "test3"),
function(x) outputOptions(output, x, suspendWhenHidden = FALSE)
Run Code Online (Sandbox Code Playgroud)
但请注意,这意味着所有计算都将立即执行.根据计算的大小,这可能会降低应用程序的性能.例如,如果有一个生成的图并且需要花费一些时间来渲染,并且只有一小部分用户会看到该图,那么在用户请求它以防止放慢速度之前将其保持暂停可能是谨慎的.计算更常用的元素.
事实证明"情节不能提前呈现,因为它们没有宽度和高度."(https://github.com/rstudio/shiny/issues/1409).所以我们必须做一些诡计来提前渲染情节:
我们可以选择我们的情节的尺寸,而不是shiny自动适应它.这意味着如果屏幕放大,情节不会增长.
library(shiny)
library(shinyjs)
create.plot <- function(mean)
{
print("working.....")
shinyjs::show('loading.div')
y <- rep(NA,10)
for(i in 1:10)
{
y[i] <- rnorm(1,mean=mean)
Sys.sleep(0.5)
}
shinyjs::hide('loading.div')
y
}
server <- function(input, output, session) {
run.function <- eventReactive(input$run,create.plot(input$mean))
output$plot.y <- renderPlot({
plot(run.function())
},
width = 72 * 5,
height = 72 * 3
)
outputOptions(output, "plot.y", suspendWhenHidden = FALSE)
}
ui <- fluidPage(shinyjs::useShinyjs(),
hidden(div(id='loading.div',h3('Loading..'))),
tabsetPanel(
tabPanel('tab1',
numericInput(inputId = 'mean',label='Mean', value=1),
actionButton(inputId = 'run', label='Run')
),
tabPanel('tab2',
plotOutput('plot.y'))
)
)
shinyApp(server=server,ui=ui)
Run Code Online (Sandbox Code Playgroud)
实际上这就是它应该工作的方式。这就是“反应式”的全部内容 - 它使用缓存进行延迟评估(因此您不必这样做)。如果你想强制计算某些东西,把它放在 a 中reactiveValue并用它计算observeEvent(这会急切执行),然后用reactive块显示值。
我修改了您的示例以在此处执行此操作:
library(shiny)
library(shinyjs)
create.plot <- function(mean) {
shinyjs::show('loading.div')
y <- rep(NA,10)
for (i in 1:10) {
y[i] <- rnorm(1,mean = mean)
Sys.sleep(0.5)
}
shinyjs::hide('loading.div')
return(list(y = y,test = 'Now it runs from tab1'))
}
server <- function(input,output,session) {
rv <- reactiveValues(plotvals=NULL)
observeEvent(input$run,{rv$plotvals <- create.plot(input$mean) })
output$plot.y <- renderPlot({
req(input$run)
plot(rv$plotvals[['y']])
})
output$test <- renderText({
rv$plotvals[['test']]
})
}
ui <- fluidPage(shinyjs::useShinyjs(),
hidden(div(id = 'loading.div',h3('Loading..'))),
tabsetPanel(
tabPanel('tab1',
numericInput(inputId = 'mean',label = 'Mean',value = 1),
actionButton(inputId = 'run',label = 'Run')
#,textOutput('test') ## Uncommenting this line, it works.
),
tabPanel('tab2',
plotOutput('plot.y'))
)
)
shinyApp(server = server,ui = ui)
Run Code Online (Sandbox Code Playgroud)
产生这个(在按下运行按钮之后):
然后当该文本消失时,您可以立即在 tab2 中看到该图。
为了更好地理解这些东西,以及反应式的懒惰性质和 的急切性质observe,请参阅 Joe Cheng 在此处的介绍。值得花时间:
https://www.rstudio.com/resources/webinars/shiny-developer-conference/
事实上,Shiny 的反应性正是让它如此高效的原因,一旦你习惯了它 - 它是相当不同的。