如何在R / Shiny流中动态创建输入,就像flowLayout中的普通输入一样?

时间:2016-08-21 18:44:15

标签: r dynamic input shiny

我混合了动态创建的输入和通常定义的输入。动态创建的输入表现得像一个大的垂直堆叠块,其他输入流动。如何让它们全部流动?

这再现了这个问题:

library(shinydashboard)
shinyApp(
  ui = dashboardPage(
    dashboardHeader(),
    dashboardSidebar(),
    dashboardBody(
      flowLayout(
        uiOutput('input_fields'),
        textInput('fielda','Field A',''),
        textInput('fieldb','Field B',''),
        textInput('fieldc','Field C',''),
        textInput('fieldd','Field D','')
      )
    )
  ),
  server = function(input, output) {
    output$input_fields <- renderUI({
      lapply(1:4, function(i) {
        textInput(paste0('field',i), paste('Field',i),'')
      })
    })
  }
)

目前的布局:

所需的布局:

编辑:忽略字段A,B,C,D小部件。它们只是用于显示其他项目如何包装,但uiOutput则作为一个块。假设所有输入都是动态的。

我想我会改用这段代码。它将renderUI移动到lapply中以创建多个通用输出(在运行时变化,最多10个),我可以使用同样通用的uiOutput语句引用它们。它甚至没有警告不存在的5个输出。不完美,但它会做。可能需要添加一个observeEvent,因为我认为它不会自行反应。

library(shinydashboard)
shinyApp(
  ui = dashboardPage(
    dashboardHeader(),
    dashboardSidebar(),
    dashboardBody(
      wellPanel(
        flowLayout(
          uiOutput('field1'),
          uiOutput('field2'),
          uiOutput('field3'),
          uiOutput('field4'),
          uiOutput('field5'),
          uiOutput('field6'),
          uiOutput('field7'),
          uiOutput('field8'),
          uiOutput('field9'),
          uiOutput('field10')
        )
      )
    )
  ),
  server = function(input, output) {
    lapply(1:5, function(i) {
      output[[paste0('field',i)]] <- renderUI({
        textInput(paste0('field',i), paste('Field',i),'')
      })
    })
  }
)

1 个答案:

答案 0 :(得分:4)

所有四个动态生成的小部件都被视为一个对象,因为它们包含在列表中。因此很难定位它们。 我能想到的最简单的解决方案是使用最新的闪亮版本中的insertUI函数,该函数可以使用devtools包安装:

devtools::install_github("rstudio/shiny")

Here您可以阅读/详细了解insertUIremoveUI

我们必须告诉insertUI它应该向用户界面添加新的小部件,我们通过指定参数selector来实现它(它必须是jQuery选择器接受的字符串)和参数where(有四个选项指定小工具相对于选择器的方式)

因此,如果我们要在Field 1下添加新的小部件,我们必须设置selector = "#fielda"(我们使用#选择器,因为我们指的是ID)。同样,我们通过设置Field 2等在selector = "#fieldb"下添加另一个小部件。按照您的命名约定,我们可以将其概括为:

selector = paste0('#field', letters[i])

在所有情况下,我们也设置where = "afterEnd。它会将新的小部件放在参考小部件下面。 (没有空间将它们放在参考小部件上方)

现在我们可以将insertUI打包成for-loop

for (i in 1:4) { 
      insertUI(
        selector = paste0('#field', letters[i]), 
        where = "afterEnd",
        ui = textInput(paste0('field',i), paste('Field',i),'')
      )
    }

请注意,我们无需在客户端添加任何uiOutput

另一个解决方案是在服务器端渲染整个dashboardBody,创建动态不同的uiOutput ...这不会令人愉快。

enter image description here

当我们看一下图片时,我们可以看到我反过来做了 - 第一行名称中的大写字母和第二行名称中的数字。我希望这不应该是一件大事。)

完整示例:

library(shiny)
library(shinydashboard)

shinyApp(
  ui = dashboardPage(
    dashboardHeader(),
    dashboardSidebar(),
    dashboardBody(
      flowLayout(
        textInput('fielda','Field A',''),
        textInput('fieldb','Field B',''),
        textInput('fieldc','Field C',''),
        textInput('fieldd','Field D','')
      )
    )
  ),
  server = function(input, output) {

    for (i in 1:4) { 
      insertUI(
        selector = paste0('#field', letters[i]), 
        where = "afterEnd",
        ui = textInput(paste0('field',i), paste('Field',i),'')
      )
    }
  }
)

修改

如果没有引用小部件,那么您还可以使用非标准评估在服务器端动态定义flowLayout

说,您想要生成三个textInput小部件。您通常可以通过键入

来完成此操作
flowLayout(
  textInput('fielda', 'Field A'),
  textInput('fieldb', 'Field B'),   
  textInput('fieldc', 'Field C')
)

在客户端。您可以通过在服务器端使用paste0函数粘贴字符串来执行相同的操作,

        i = 1:3
        UI <- paste0("flowLayout(",
                paste0("textInput(", 
                       "'field", letters[i], "', ",
                       paste0("'Field ", LETTERS[i], "'"), 
                       ")",
                       collapse = ", "),
              ")")

将其保存在变量中,例如UI,然后解析字符串并最终对其进行评估。

eval(parse(text = UI))

在下面的示例中,您有15个动态生成的小部件

enter image description here

完整示例:

library(shiny)
library(shinydashboard)

shinyApp(
  ui = dashboardPage(
    dashboardHeader(),
    dashboardSidebar(),
    dashboardBody(
      wellPanel(
      uiOutput("ui1")
    )
   )
  ),
  server = function(input, output) {

      output[["ui1"]] <- renderUI({
        i = 1:15
        UI <- paste0("flowLayout(",
                paste0("textInput(", 
                       "'field", letters[i], "', ",
                       paste0("'Field ", LETTERS[i], "'"), 
                       ")",
                       collapse = ", "),
              ")")
        # print(UI)
        eval(parse(text = UI))
      })

  }
)