Passing the same reactive object to the same shiny module causes "Error: promise already under evaluation"

Passing the same reactive object to the same shiny module causes "Error: promise already under evaluation"

Objective: 我创建了一个简单的可复制应用程序,我试图在其中通过操作按钮添加 UI 组件,以便我可以过滤UI 过滤器从操作按钮生成的相同数据集。我试图在应用过滤器后使用闪亮的模块代码来保存数据集,并在下次单击操作按钮时重用过滤后的数据集。换句话说,每次单击操作按钮生成一组新的 UI 组件时,我都想重用这个过滤后的数据集(而不是原始的未过滤数据集)。

问题: 当用户单击操作按钮时,预期的结果在第一个实例中有效,但任何连续单击操作按钮都会导致 错误:promise already评估中:递归默认参数引用或更早的问题? 是我试图在 shiny/shiny 模块中做不到的事情,还是我执行的不正确?任何帮助将不胜感激。

library(shiny)
library(dplyr)

add.filter.UI = function(id) {

  ns = NS(id)

  fluidRow(
    column(4, uiOutput(ns("UI_1"))),
    column(6, uiOutput(ns("UI_2"))),
    column(width = 2,
           actionButton(inputId = ns("rm.filter"), label = "Filter", icon = icon("minus"), style = "position: relative; bottom: 0; right:0; top:24px;")),
    br(),
    column(width = 12, tableOutput(ns("test"))))

}

add.filter.server = function(id, data) {
  moduleServer(id, function(input, output, session) {

    ns = session$ns

    output$UI_1 <- renderUI({
      selectInput(inputId = ns("sel.col"),
                  label = "Select a column",
                  choices =  names(data %>% select_if(is.numeric)),
                  multiple = F)
    })

    col.rng = reactive({ data %>% select(one_of(input$sel.col)) })

    output$UI_2 = renderUI({

      sliderInput(inputId = ns("sel.rng"),
                  label = "Filter the range",
                  min = min(col.rng(), na.rm = T),
                  max = max(col.rng(), na.rm = T),
                  value = c(min(col.rng(), na.rm = T), max(col.rng(), na.rm = T)),
                  step = (max(col.rng(), na.rm = T) - min(col.rng(), na.rm = T)) / 100 # of breaks
      )
    })

    data.filtered = reactive({

      data %>%
        rename(Var = one_of(input$sel.col)) %>%
        arrange(Var) %>%
        filter(Var >= min(input$sel.rng), Var <= max(input$sel.rng)) %>%
        rename(!!input$sel.col := Var)

    })

    output$test = renderTable({

      data.filtered() %>%
        head()

    })

    return( data.filtered )

  })

}

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      column(width = 2, offset = 10, actionButton(inputId = "add.filter", label = "Filter", icon = icon("plus"), style = "position:relative; left:10px;")),
      tags$div(id = 'placeholder')
    ),
    mainPanel(
      tableOutput(outputId = "tbl")
    )
  )
)

server <- function(input, output, session) {

  counter = reactiveVal(value = 0)

  observeEvent(input$add.filter, {

    id <- paste0("#filter_", input$add.filter)  # - 1, "-break"

    insertUI(selector = "#placeholder",
             where = "afterEnd",
             ui = tags$div(
               add.filter.UI(paste0("filter_", input$add.filter)),
               id = id)
    )

    counter(input$add.filter)

    if (counter() == 1) {

      df.filtered = add.filter.server(id = paste0("filter_", input$add.filter), data = mtcars)

    } else {

      df.filtered = add.filter.server(id = paste0("filter_", input$add.filter), data = df.filtered())

    }

    output$tbl = renderTable({

      df.filtered()

    })

  })

}

# Run the app ----
shinyApp(ui = ui, server = server)

我能够通过采取替代方法解决我的问题,如我在上面的评论中所述。下面的代码提供了用户选择的输入的摘要 table。所有需要做的就是将这些过滤器应用于 table 以相应地对其进行子集化。

library(shiny)
library(dplyr)

add.filter.UI = function(id) {

  ns = NS(id)

  fluidRow(
    column(4, uiOutput(ns("UI_1"))),
    column(6, uiOutput(ns("UI_2"))),
    column(width = 2,
           actionButton(inputId = ns("rm.filter"), label = "Filter", icon = icon("minus"), style = "position: relative; bottom: 0; right:0; top:24px;"))
    )

}

add.filter.server = function(id, data) {
  moduleServer(id, function(input, output, session) {

    ns = session$ns

    output$UI_1 <- renderUI({
      selectInput(inputId = ns("sel.col"),
                  label = "Select a column",
                  choices =  names(data %>% select_if(is.numeric)),
                  multiple = F)
    })

    col.rng = reactive({ data %>% select(one_of(input$sel.col)) })

    output$UI_2 = renderUI({

      sliderInput(inputId = ns("sel.rng"),
                  label = "Filter the range",
                  min = min(col.rng(), na.rm = T),
                  max = max(col.rng(), na.rm = T),
                  value = c(min(col.rng(), na.rm = T), max(col.rng(), na.rm = T)),
                  step = (max(col.rng(), na.rm = T) - min(col.rng(), na.rm = T)) / 100 # of breaks
      )
    })

    data.filtered = reactive({

      data.frame(Col.Nm = input$sel.col,
                 Min = min(input$sel.rng, na.rm = T),
                 Max = max(input$sel.rng, na.rm = T))

    })

    return( data.filtered )

  })

}

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      column(width = 2, offset = 10, actionButton(inputId = "add.filter", label = "Filter", icon = icon("plus"), style = "position:relative; left:10px;")),
      tags$div(id = 'placeholder')
    ),
    mainPanel(
      tableOutput(outputId = "tbl")
    )
  )
)

server <- function(input, output, session) {

  df.filtered = reactiveValues()

  observeEvent(input$add.filter, {

    id <- paste0("#filter_", input$add.filter)  # - 1, "-break"

    insertUI(selector = "#placeholder",
             where = "afterEnd",
             ui = tags$div(
               add.filter.UI(paste0("filter_", input$add.filter)),
               id = id)
    )

    df.filtered[[paste0("Filtered_", input$add.filter[1])]] = add.filter.server(id = paste0("filter_", input$add.filter), data = mtcars)

    output$tbl = renderTable({

      for (i in 1:input$add.filter[1]) {

        if (i == 1) {

          df = df.filtered[[paste0("Filtered_", i)]]()

        } else {

          df = rbind(df,
                     df.filtered[[paste0("Filtered_", i)]]())

        }

      }

      df

    })


  })

}

# Run the app ----
shinyApp(ui = ui, server = server)