我遇到了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)
为了确保问题是用户在tab1时没有查看输出,我创建了一个测试字符串。如果取消注释该行,则问题是“修复”:该功能在用户点击按钮后直接运行。但是,这只能起作用,因为他正在查看tab1中的输出。
这是一个严重的问题,因为我有一个闪亮的仪表板,有很多标签。有带输入的选项卡和带输出的选项卡。我希望在单击按钮时运行该功能,但只有在用户进入“输出”选项卡时才会运行。
这是一个已知问题吗?有解决方法吗?
答案 0 :(得分:10)
不可见的反应元素被视为“已暂停”,并且在取消暂停之前不会更新其值。默认情况下,只有在屏幕上呈现它们才会发生这种情况。
即使不可见,也可以通过更改其暂停状态来设置要更新的元素。您可以使用outputOptions
函数执行此操作。
对于output$test
元素,您需要运行
outputOptions(output, "test", suspendWhenHidden = FALSE)
如果您想要更新多个元素,即使不可见,也可以使用
lapply(c("test1", "test2", "test3"),
function(x) outputOptions(output, x, suspendWhenHidden = FALSE)
但请注意,这意味着所有计算都将立即执行。根据计算的大小,这可能会降低应用程序的性能。例如,如果有一个生成的图并且需要花费一些时间来渲染,并且只有一小部分用户会看到该图,那么在用户请求它以防止放慢速度之前将其保持暂停可能是谨慎的。计算更常用的元素。
事实证明“由于没有宽度和高度,情节不能提前渲染。”(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)
答案 1 :(得分:2)
Actuall就是它的工作方式。这就是“反应性”的全部意义 - 它通过缓存进行惰性评估(所以你不必这样做)。如果要强制计算某些内容,请将其放在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)
产生这个(按下运行按钮后):
然后当该文本消失时,您可以立即在tab2中看到该情节。
为了更好地理解这些东西,以及被动的惰性和observe
的热切性质,请参阅Joe Cheng在此处的介绍。值得花时间:
https://www.rstudio.com/resources/webinars/shiny-developer-conference/
事实上,Shiny的反应性正是使它变得如此富有成效的原因,一旦你习惯了它,它就会变得非常不同。