【问题标题】:R shiny dynamic UI in insertUIinsertUI中的R闪亮动态UI
【发布时间】:2019-04-01 18:35:38
【问题描述】:

我有一个闪亮的应用程序,我想在其中使用操作按钮添加一个 UI 元素,然后让插入的 ui 是动态的。

这是我当前的 ui 文件:

library(shiny)

shinyUI(fluidPage(
  div(id="placeholder"),
  actionButton("addLine", "Add Line")
))

和服务器文件:

library(shiny)

shinyServer(function(input, output) {
  observeEvent(input$addLine, {
    num <- input$addLine
    id <- paste0("ind", num)
    insertUI(
      selector="#placeholder",
      where="beforeBegin",
      ui={
         fluidRow(column(3, selectInput(paste0("selected", id), label=NULL, choices=c("choice1", "choice2"))))
      })
  })

})

如果在特定的 ui 元素中选择了choice1,我想在行中添加一个textInput。如果在 ui 元素中选择了choice2,我想添加一个numericInput。

虽然我通常了解如何创建响应用户输入而变化的反应性值,但我不知道在这里做什么,因为我不知道如何观察尚未创建的元素,而且我不知道知道的名字。任何帮助将不胜感激!

【问题讨论】:

    标签: r shiny


    【解决方案1】:

    代码

    这可以通过modules轻松解决:

    library(shiny)
    
    row_ui <- function(id) {
      ns <- NS(id)
      fluidRow(
        column(3, 
               selectInput(ns("type_chooser"), 
                           label = "Choose Type:", 
                           choices = c("text", "numeric"))
        ),
        column(9,
               uiOutput(ns("ui_placeholder"))
        )
      )
    } 
    
    row_server <- function(input, output, session) {
      return_value <- reactive({input$inner_element})
      ns <- session$ns
      output$ui_placeholder <- renderUI({
        type <- req(input$type_chooser)
        if(type == "text") {
          textInput(ns("inner_element"), "Text:")
        } else if (type == "numeric") {
          numericInput(ns("inner_element"), "Value:", 0)
        }
      })
    
      ## if we later want to do some more sophisticated logic
      ## we can add reactives to this list
      list(return_value = return_value) 
    }
    
    ui <- fluidPage(  
      div(id="placeholder"),
      actionButton("addLine", "Add Line"),
      verbatimTextOutput("out")
    )
    
    server <- function(input, output, session) {
      handler <- reactiveVal(list())
      observeEvent(input$addLine, {
        new_id <- paste("row", input$addLine, sep = "_")
        insertUI(
          selector = "#placeholder",
          where = "beforeBegin",
          ui = row_ui(new_id)
        )
        handler_list <- isolate(handler())
        new_handler <- callModule(row_server, new_id)
        handler_list <- c(handler_list, new_handler)
        names(handler_list)[length(handler_list)] <- new_id
        handler(handler_list)
      })
    
      output$out <- renderPrint({
        lapply(handler(), function(handle) {
          handle()
        })
      })
    }
    
    shinyApp(ui, server)
    

    说明

    模块是一段模块化的代码,您可以根据需要多次重复使用它,而不必担心唯一名称,因为模块在namespaces 的帮助下处理了这些问题。

    一个模块由两部分组成:

    1. UI 函数
    2. server 函数

    它们与普通的 UI 和 server 函数非常相似,但需要注意一些事项:

    • namespacing:在服务器中,您可以像往常一样从UI 访问元素,例如input$type_chooser。但是,在UI 部分,您必须使用NS 来namespace 您的元素,它会返回一个您可以方便地在其余代码中使用的函数。为此,UI 函数采用参数id,可以将其视为此模块任何实例的(唯一)命名空间。元素 ids 在模块中必须是唯一的,并且由于命名空间,它们在整个应用程序中也是唯一的,即使您使用模块的多个实例。
    • UI:因为你的UI 是一个函数,它只有 one 返回值,如果你想返回多个元素,你必须将你的元素包装在 tagList 中(这里不需要)。
    • server:您需要 session 参数,否则它是可选的。如果您希望您的模块与主应用程序通信,您可以传入一个(反应式)参数,您可以在模块中照常使用该参数。同样,如果您希望您的主应用程序使用模块中的某些值,您应该返回反应式,如代码所示。如果您想从服务器函数创建 UI 元素,您还需要命名它们,并且您不能通过 session$ns 访问命名空间函数,如图所示。
    • usage:要使用您的模块,您可以在主应用程序中插入UI 部分,方法是使用唯一的id 调用函数。然后你必须调用callModule 来使服务器逻辑工作,你传入相同的id。此调用的返回值是您的模块服务器函数的returnValue,并且可以在主应用程序中使用模块内的值。

    这简要解释了模块。 here.

    【讨论】:

    • 谢谢,完美解决了我的问题。看来我肯定需要学习模块的工作原理!
    • 事实证明,模块已经解决了很多我现有的闪亮问题,包括我已经放弃的问题-谢谢!
    【解决方案2】:

    您可以使用insertUI() 或renderUI()。 insertUI() 如果您想添加多个相同类型的 ui,那非常棒,但我认为这不适用于您。 我认为您要么想要添加数字输入,要么想要添加文本输入,而不是两者。

    因此,我建议使用renderUI():

      output$insUI <- renderUI({
          req(input$choice)
          if(input$choice == "choice1") return(fluidRow(column(3,
             textInput(inputId = "text", label=NULL, "sampleText"))))
          if(input$choice == "choice2") return(fluidRow(column(3, 
             numericInput(inputId = "text", label=NULL, 10, 1, 20))))
      })
    

    如果您更喜欢使用insertUI(),您可以使用:

    observeEvent(input$choice, {
      if(input$choice == "choice1") insUI <- fluidRow(column(3, textInput(inputId 
                                    = "text", label=NULL)))
      if(input$choice == "choice2") insUI <- fluidRow(column(3, 
                                    numericInput(inputId = "text", label=NULL, 10, 1, 20)))
    
      insertUI(
        selector="#placeholderInput",
        where="beforeBegin",
        ui={
          insUI
        })
    })
    

    在 ui 端:div(id="placeholderInput").

    完整代码如下:

    library(shiny)
    
    ui <- shinyUI(fluidPage(
      div(id="placeholderChoice"),
      uiOutput("insUI"),
      actionButton("addLine", "Add Line")
    ))
    
    
    server <- shinyServer(function(input, output) {
      observeEvent(input$addLine, {
        insertUI(
          selector="#placeholderChoice",
          where="beforeBegin",
          ui={
            fluidRow(column(3, selectInput(inputId = "choice", label=NULL, 
                     choices=c("choice1", "choice2"))))
          })
      })
    
      output$insUI <- renderUI({
          req(input$choice)
          if(input$choice == "choice1") return(fluidRow(column(3,
             textInput(inputId = "text", label=NULL, "sampleText"))))
          if(input$choice == "choice2") return(fluidRow(column(3, 
             numericInput(inputId = "text", label=NULL, 10, 1, 20))))
      })
    
    })
    
    shinyApp(ui, server)
    

    【讨论】:

    • 感谢您的关注,非常感谢。这与我正在尝试做的有点不同。我希望“添加行”按钮在每次单击时插入一个选项、值对。因此,对于每个 selectInput 小部件,都有一个对应的 textInput 或 numericInput
    • 因此,如果我单击按钮 3 次,将有 3 对单独的小部件(按钮除外)。它们中的每一个都会根据 selectInput 的选择显示一个 textInput 或一个 numericInput(彼此独立)
    • 嗯,我不确定我是否会在接下来的几天里找到时间。从你对我的问题来看,这并不清楚。但我猜@thotal 提供了一个答案。
    【解决方案3】:

    不幸的是,我还不能对答案发表评论,但我认为像我这样发现这个问题的人可能想知道这一点:@thotal 的答案对我有用,除了一行:new_handler &lt;- callModule(row_server, new_id) 给了我一个错误:“警告:模块中的错误: 未使用的参数 (childScope$output, childScope)"

    看了一圈发现this stackoverflow question,给出了基本使用new_handler &lt;- row_server(new_id)的解决方案。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-23
    • 2014-04-21
    • 2021-05-06
    • 1970-01-01
    • 1970-01-01
    • 2020-07-21
    相关资源
    最近更新 更多