循环渲染动态加载的UI仅显示最后一个值

时间:2019-03-18 12:42:29

标签: r shiny

我有一个Shiny应用程序,可以动态加载任意数量的outputPlot UI。在下面的示例中,我只是迭代字母表的前三个字母。

要在动态加载的UI中渲染图,我在循环中这样调用renderPlot

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

但是结果是所有三个outputPlot(pApBpC)都是用plot.df(df, 'C'))渲染的。在循环看来,renderPlot之后a = 'C'就呈现出来了。相反,应该分别用pApBpC渲染输出UI plot.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)

1 个答案:

答案 0 :(得分:2)

您必须使用local(请参阅here)。

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