【问题标题】:In delayed expression evaluation, R Shiny uses changed values of variables在延迟表达式评估中,R Shiny 使用变量的变化值
【发布时间】:2018-01-07 08:11:42
【问题描述】:

这是一个简单的问题演示:

library(shiny)

ui <- fluidPage(
    textOutput("Text1"),
    textOutput("Text2")
)

server <- function(input, output) {
    for(i in 1:2) {
        id <- paste0("Text", i)
        output[[id]] <- renderText(paste0("This is text #", i)) # Problem!
    }
}

shinyApp(ui, server)

这个程序产生输出

这是文本 #2
这是文字#2

而不是#1 和#2。

显然,Shiny 将传递给 renderText() 的表达式存储在标记为 # Problem! 的行中,并在 for 循环结束后评估它们。表达式依赖于变量i,其最终值i = 2 用于计算两个表达式。

如何在仍然使用循环的同时产生正确的输出(如何强制 Shiny 在不同的表达式中使用不同的 i 值)?在我的代码中,循环限制是动态的,我不能用几个静态调用替换循环。

【问题讨论】:

    标签: r shiny


    【解决方案1】:

    为什么for-loop 不起作用,请检查此示例的输出:

    library(shiny)
    
    ui <- fluidPage(
      textOutput("Text1"),
      textOutput("Text2")
    )
    
    server <- function(input, output) {
      for(i in 1:3) {
        id <- paste0("Text", i)
        output[[id]] <- renderText(paste0("This is text #", i)) # Problem!
      }
      i=10 # we set i to 10.
    
    }
    
    shinyApp(ui, server)
    

    如您所见,所有 renderText 元素都使用 i 的最后一个(全局)值。 lapply 中的情况并非如此,其中将参数传递给函数,但该参数未在全局环境中定义。

    所以你可以使用lapply 而不是for-loop,像这样:

    library(shiny)
    
    ui <- fluidPage(
      textOutput("Text1"),
      textOutput("Text2")
    )
    
    server <- function(input, output) {
    
      lapply(1:2,function(i){
        id <- paste0("Text", i)
        output[[id]] <- renderText(paste0("This is text #", i)) # Problem!
      })
    
    }
    
    shinyApp(ui, server)
    

    输出:


    如果您还希望 ui 具有响应性,可以使用 renderUIuiOutput,例如如下:

    library(shiny)
    
    ui <- fluidPage(
      numericInput("number_of_text","Number of text",min=1,max=10,value=3),
      uiOutput('my_text')
    )
    
    server <- function(input, output) {
    
    
      reactive_text <- reactive({
        all_text <- lapply(1:input$number_of_text,function(i){
          id <- paste0("Text", i)
          renderText(paste0("This is text #", i)) # Problem!
        })
       # do.call(all_text,tagList)
      })
    
      output$my_text <- renderUI({
        do.call(fluidRow, reactive_text())
      })
    
    }
    
    shinyApp(ui, server)
    

    输出:

    希望这会有所帮助!

    【讨论】:

    • 1.哇,这太快了!谢谢! 2. 是的,我知道动态 UI,基于 RenderUIuiOutput。但是很高兴在这里有明确的例子。 3. 您能否解释一下为什么lapply 的行为与循环不同?
    • 嗨,亚历克斯,我在答案的开头添加了解释。我希望这能说清楚。
    • 好的,从您添加的解释中,我看到魔法不在lapply 中,而是在辅助函数中调用renderText。确实,这段代码不像lapply那样优雅,但也能正常工作:foo &lt;- function(i, output) { id &lt;- paste0("Text", i); output[[id]] &lt;- renderText(paste0("This is text #", i)) }; server &lt;- function(input, output) { for(i in 1:2) { foo(i, output) } }
    • 我认为该解决方案同样优雅,这是一个非常聪明的解决方案。看来我也学到了一些东西;)
    • 仅供参考,我写了一个关于为什么会发生这种情况的详细解释 - community.rstudio.com/t/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多