【问题标题】:Generate and Display dynamic UI using for loop in Shiny在 Shiny 中使用 for 循环生成和显示动态 UI
【发布时间】:2021-01-11 04:01:26
【问题描述】:

我将数据框作为输入,并在其中添加了一些额外的列。然后我使用 for 循环对每一行进行处理。此处理需要时间,因此我想将处理后的行显示为单独的 ui 元素。

例如, 如果正在处理第一行,则

ROW-1

如果正在处理第 2 行,则将其添加到下一个 ROW-1

ROW-1
ROW-2

所以当我的 for 循环在行中进行时,最新处理的行将作为 UI 元素添加到列表中。

我使用了 lapply,但它会处理所有行,然后将所有内容一起显示。

library(shiny)

ui <- fluidPage(
  uiOutput("ui1")
)

server <- function(input, output) {
  
  Init_vals <- 1:5
  for(i in 1:5){
Sys.sleep(5)
    output[[paste("ui",i,sep="")]] = renderUI({
        list(
          fluidPage(
            fluidRow(
              textInput(
                inputId=paste0("id",i) ,
                label = "here",
                value = Init_vals[i]
              )
            )
          ),
          uiOutput(paste("ui",i + 1,sep=""))
        )
        
      })
  }

    
  
}


shinyApp(ui = ui, server = server)

在这里,你可以看到只生成了最后一个ui元素,其他从ui1到ui4的元素都被跳过了!应用应每 5 秒生成新的文本输入。

【问题讨论】:

标签: r shiny shiny-server shinyapps shiny-reactivity


【解决方案1】:

像 Jim 建议的那样,尝试使用 lapply 而不是 for 循环:

server <- function(input, output) {
    lapply(1:5, function(i) {
        Sys.sleep(5)
        output[[paste0("ui", i)]] <- renderUI({
            list(
              fluidPage(
                fluidRow(
                  textInput(
                    inputId = paste0("id", i),
                    label = "here",
                    value = i
                  )
                )
              ),
              uiOutput(paste0("ui", i + 1))
            )
        })
    })
}

似乎有一些关于闪亮的独特之处,它会阻止输出被正确分配for 循环。你也可以使用purrr::walk()

编辑: 你是对的,shiny 等待显示所有内容,直到 server 函数完成运行。我相信这就是 shiny 的设计方式——动态 UI 元素(使用 renderUI 创建)将显示,直到它们全部创建完成。

如果您的目标是告知您的用户该过程可能需要多长时间,您可能希望使用 withProgress()

server <- function(input, output) {
  withProgress(
    message = 'Calculation in progress',
    detail = 'This may take a while...',
    value = 0, 
    expr = {
      lapply(1:5, function(i) {
        Sys.sleep(5)
        output[[paste0("ui", i)]] <- renderUI({
          list(
            fluidPage(
              fluidRow(
                textInput(
                  inputId = paste0("id", i),
                  label = "here",
                  value = i
                )
              )
            ),
            uiOutput(paste0("ui", i + 1))
          )
        })
        incProgress(1 / 5)
      })
  })
}

【讨论】:

  • 在您的 lapply 以及我的方法中,它完成了整个过程(25 秒)并完全显示了输出。我的方法也会发生同样的事情。我想在 5 秒后或在 for 循环中完成迭代时创建每个元素。
  • 啊,我想我现在更了解您的用例了。查看我的更新答案
  • 我已经有了这个功能,但我想显示 ui 元素,因为它们是由 for 循环处理的。例如,如果我有 10 行的数据框并且我正在使用 for 循环来处理它,我想在处理完成时显示第一行,然后我将第二行向下到第一行,然后是第三行,这样所有 10 行将一一显示。
  • 我想这样做是因为所有的行也会有 ui 元素。如果在处理过程中缺少任何信息,它将通过提示询问用户输入。我无法使用 for 循环或 lapply 实现这一点,因为它们都不会在旅途中显示已完成的行或允许我在 for 循环处理期间生成提示。您能否建议一些具有反应性的框架,或者我如何使用反应性值来构造 for 循环逻辑。提前致谢!
  • 我了解您的用例,但我认为这需要对shiny 进行黑客攻击,以至于我认为我无法为您提供。如果您想对元素的呈现进行如此多的控制,您可以考虑创建自定义 javascript 代码
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-11
  • 2018-11-11
  • 2021-08-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多