首页 > 解决方案 > 在循环中呈现动态加载的 UI 仅显示最后一个值

问题描述

我有一个闪亮的应用程序,它可以动态加载任意数量的outputPlotUI。在下面的示例中,我只是对字母表的前三个字母进行迭代。

为了在动态加载的 UI 中渲染绘图,我这样调用renderPlot了一个循环:

for (a in LETTERS[1:3]) {
 output[[paste0('p',a)]] <-  renderPlot(plot.df(df, a))
}   

但结果是所有三个 outputPlot (pApB)pC都用. 在我看来,是在循环完成后呈现的,并且. 相反,输出 UI和应该分别用、和呈现。但在查看输出时显然不是这种情况。plot.df(df, 'C'))renderPlota = 'C'pApBpCplot.df(df, 'A')plot.df(df, 'B')plot.df(df, 'C')

如果输出 UI 是一个模块并且在循环调用中,我以前已经成功了callModule,这会以某种方式强制评估参数。但我现在试图避免为我的输出 UI 创建一个单独的模块。

完全可重现的例子

library(shiny)
library(dplyr)

# Define UI for application that draws a histogram
ui <- fluidPage(
    plotOutput('pltA'),plotOutput('pltB'),plotOutput('pltC'),
    tags$hr(),
    tags$div(id='placeholder')
)

col <- c(A='#66bd63', B='#fdae61', C='#74add1')
plot.df <- function(df, a) {
    #browser()
    df <- filter(df, letter==a)
    if (nrow(df) == 0) return()
    plot(df$i, df$y, type='p', col=col[a], pch=19, main=a)
}

# Define server logic required to draw a histogram
server <- function(input, output, session) {

    my_data <- reactiveVal(data.frame())
    autoInvalidate <- reactiveTimer(2000)

    # Generate some random data and assign to one of three different letters:
    observe({
        autoInvalidate()

        a <- sample(LETTERS[1:3], 1)
        data.frame(y=rnorm(5), letter=a) %>%
            bind_rows(isolate(my_data())) %>%
            group_by(letter) %>%
            mutate(i=seq_along(y)) %>%
            my_data
    })

    # Proof of function making a plot.
    output$pltA <- renderPlot(plot.df(my_data(), 'A'))
    output$pltB <- renderPlot(plot.df(my_data(), 'B'))
    output$pltC <- renderPlot(plot.df(my_data(), 'C'))

    # Dynamically load output UIs.
    observe({
      let <- unique(my_data()$letter)
      if (is.null(let)) return()

      for (l in let) {
          if (is.null(session$clientData[[paste0('output_p',l,'_hidden')]])) {
              insertUI('#placeholder', 'beforeEnd', ui=plotOutput(paste0('p',l)))
          }
      }
    })

    # Update dynamically loaded plots
    observe({
        df <- my_data()
        if (nrow(df) == 0) return()

        for (a in LETTERS[1:3]) {
            cat('Updating ', a, '\n')
            output[[paste0('p',a)]] <- renderPlot(plot.df(df, a))
        }
    })
}

# Run the application 
shinyApp(ui = ui, server = server)

标签: rshiny

解决方案


你必须使用local(见这里)。

for (a in LETTERS[1:3]) {
  local({
    aa <- a
    output[[paste0('p',aa)]] <- renderPlot(plot.df(df, aa))
  })
}

推荐阅读