首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >R Shiny:如何动态追加任意数量的输入小部件

R Shiny:如何动态追加任意数量的输入小部件
EN

Stack Overflow用户
提问于 2016-06-29 02:04:39
回答 4查看 4.7K关注 0票数 13

目标

我正在开发一个闪亮的应用程序,它允许用户上传自己的数据,并通过提供数据过滤小部件来关注整个数据或子集,如下图所示

选择输入"Variable 1“将显示用户上传的数据的所有列名,选择输入"Value”将显示在"Variable 1“中选择的相应列的所有唯一值。理想情况下,用户可以通过某种触发器添加尽可能多的行("Variable X“+ "Value"),一种可能是单击"Add more”操作按钮。

一种可能的解决方案

在网上查找后,我发现了一个由给出的很有前途的解决方案,粘贴在下面

ui.R

代码语言:javascript
复制
library(shiny)

shinyUI(pageWithSidebar(

    # Application title
    headerPanel("Dynamically append arbitrary number of inputs"),

    # Sidebar with a slider input for number of bins
    sidebarPanel(
        uiOutput("allInputs"),
        actionButton("appendInput", "Append Input")
    ),

    # Show a plot of the generated distribution
    mainPanel(
        p("The crux of the problem is to dynamically add an arbitrary number of inputs
          without resetting the values of existing inputs each time a new input is added.
          For example, add a new input, set the new input's value to Option 2, then add
          another input. Note that the value of the first input resets to Option 1."),

        p("I suppose one hack would be to store the values of all existing inputs prior
          to adding a new input. Then,", code("updateSelectInput()"), "could be used to 
          return inputs to their previously set values, but I'm wondering if there is a 
          more efficient method of doing this.")
    )
))

server.R

代码语言:javascript
复制
library(shiny)

shinyServer(function(input, output) {

    # Initialize list of inputs
    inputTagList <- tagList()

    output$allInputs <- renderUI({
        # Get value of button, which represents number of times pressed
        # (i.e. number of inputs added)
        i <- input$appendInput
        # Return if button not pressed yet
        if(is.null(i) || i < 1) return()
        # Define unique input id and label
        newInputId <- paste0("input", i)
        newInputLabel <- paste("Input", i)
        # Define new input
        newInput <- selectInput(newInputId, newInputLabel,
                                c("Option 1", "Option 2", "Option 3"))
        # Append new input to list of existing inputs
        inputTagList <<- tagAppendChild(inputTagList, newInput)
        # Return updated list of inputs
        inputTagList
    })

})

不利的一面

正如自己指出的那样,每次添加新的输入窗口小部件时,所有现有的输入窗口小部件都会意外地被重置。

Shiny中一种有前途的数据子集/过滤解决方案

正如所建议的,包中的datatable函数提供了一种很好的方法来过滤Shiny中的数据。请参阅以下启用了数据过滤的最小示例。

代码语言:javascript
复制
library(shiny)
shinyApp(
    ui = fluidPage(DT::dataTableOutput('tbl')),
    server = function(input, output) {
        output$tbl = DT::renderDataTable(
            iris, filter = 'top', options = list(autoWidth = TRUE)
        )
    }
)

如果你要在你闪亮的应用程序中使用它,有一些重要的方面值得注意。

对于数字/日期/时间列,

  1. 筛选框类型为
    • :范围滑块用于筛选范围内的行
    • 用于因子列:
    • 输入用于显示所有可能的选择字符列:普通搜索框为used

  1. How to obtain filtered data
    • 假设表的输出id为tableId,则使用input$tableId_rows_all作为所有页面上的行的索引(在表被搜索字符串过滤之后)。请注意, input$tableId_rows_all 返回DT (>= 0.1.26)的所有页面上的行的索引。如果您使用的是常规DT版本,只有当前页面的索引是returned**
    • To install install.packages('DT')**,DT (>= 0.1.26),请参考其GitHub page

  1. here

  • 如果数据有很多列,则列宽和筛选框宽度都会变窄,因此很难将文本显示为报表列

仍待解决

尽管存在一些已知的问题,包中的datatable仍然是Shiny中数据子集的一个很有前途的解决方案。这个问题本身,也就是如何在Shiny中动态添加任意数量的输入小部件,然而,是有趣的,也是具有挑战性的。在人们找到解决这个问题的好方法之前,我将把这个问题保留下来:)

谢谢!

EN

回答 4

Stack Overflow用户

发布于 2018-06-23 04:14:56

你在找这样的东西吗?

代码语言:javascript
复制
library(shiny)


LHSchoices <- c("X1", "X2", "X3", "X4")


#------------------------------------------------------------------------------#

# MODULE UI ----
variablesUI <- function(id, number) {

  ns <- NS(id)

  tagList(
    fluidRow(
      column(6,
             selectInput(ns("variable"),
                         paste0("Select Variable ", number),
                         choices = c("Choose" = "", LHSchoices)
             )
      ),

      column(6,
             numericInput(ns("value.variable"),
                          label = paste0("Value ", number),
                          value = 0, min = 0
             )
      )
    )
  )

}

#------------------------------------------------------------------------------#

# MODULE SERVER ----

variables <- function(input, output, session, variable.number){
  reactive({

    req(input$variable, input$value.variable)

    # Create Pair: variable and its value
    df <- data.frame(
      "variable.number" = variable.number,
      "variable" = input$variable,
      "value" = input$value.variable,
      stringsAsFactors = FALSE
    )

    return(df)

  })
}

#------------------------------------------------------------------------------#

# Shiny UI ----

ui <- fixedPage(
  verbatimTextOutput("test1"),
  tableOutput("test2"),
  variablesUI("var1", 1),
  h5(""),
  actionButton("insertBtn", "Add another line")

)

# Shiny Server ----

server <- function(input, output) {

  add.variable <- reactiveValues()

  add.variable$df <- data.frame("variable.number" = numeric(0),
                                "variable" = character(0),
                                "value" = numeric(0),
                                stringsAsFactors = FALSE)

  var1 <- callModule(variables, paste0("var", 1), 1)

  observe(add.variable$df[1, ] <- var1())

  observeEvent(input$insertBtn, {

    btn <- sum(input$insertBtn, 1)

    insertUI(
      selector = "h5",
      where = "beforeEnd",
      ui = tagList(
        variablesUI(paste0("var", btn), btn)
      )
    )

    newline <- callModule(variables, paste0("var", btn), btn)

    observeEvent(newline(), {
      add.variable$df[btn, ] <- newline()
    })

  })

  output$test1 <- renderPrint({
    print(add.variable$df)
  })

  output$test2 <- renderTable({
    add.variable$df
  })

}

#------------------------------------------------------------------------------#

shinyApp(ui, server)
票数 5
EN

Stack Overflow用户

发布于 2019-05-10 23:44:37

如果您正在寻找Shiny模块中的数据子设置/过滤: :

来自package shinytoolsfilterData可以完成这项工作。它返回一个call形式的表达式,但也可以返回数据(如果您的数据集不是太大的话)。

代码语言:javascript
复制
library(shiny)
# remotes::install_github("ardata-fr/shinytools")
library(shinytools)

ui <- fluidPage(
  fluidRow(
    column(
      3,
      filterDataUI(id = "ex"),
      actionButton("AB", label = "Apply filters")
    ),
    column(
      3,
      tags$strong("Expression"),
      verbatimTextOutput("expression"),
      tags$br(),
      DT::dataTableOutput("DT")
    )
  )
)

server <- function(input, output) {

  x <- reactive({iris})

  res <- callModule(module = filterDataServer, id = "ex", x = x, return_data = FALSE)

  output$expression <- renderPrint({
    print(res$expr)
  })

  output$DT <- DT::renderDataTable({
    datatable(data_filtered())
  })

  data_filtered <- eventReactive(input$AB, {
    filters <- eval(expr = res$expr, envir = x())
    x()[filters,]

  })
}

shinyApp(ui, server)

您还可以使用lazyevalrlang来计算表达式:

代码语言:javascript
复制
filters <- lazyeval::lazy_eval(res$expr, data = x())
filters <- rlang::eval_tidy(res$expr, data = x())
票数 2
EN

Stack Overflow用户

发布于 2018-09-03 08:43:57

您需要检查现有输入值并使用它们(如果可用):

代码语言:javascript
复制
  # Prevent dynamic inputs from resetting
  newInputValue <- "Option 1"
  if (newInputId %in% names(input)) {
    newInputValue <- input[[newInputId]]
  }
  # Define new input
  newInput <- selectInput(newInputId, newInputLabel, c("Option 1", "Option 2", "Option 3"), selected=newInputValue)

可以在这里找到gist的工作版本(没有重置问题):https://gist.github.com/motin/0d0ed0d98fb423dbcb95c2760cda3a30

复制如下:

ui.R

代码语言:javascript
复制
library(shiny)

shinyUI(pageWithSidebar(

  # Application title
  headerPanel("Dynamically append arbitrary number of inputs"),

  # Sidebar with a slider input for number of bins
  sidebarPanel(
    uiOutput("allInputs"),
    actionButton("appendInput", "Append Input")
  ),

  # Show a plot of the generated distribution
  mainPanel(
    p("This shows how to add an arbitrary number of inputs
      without resetting the values of existing inputs each time a new input is added.
      For example, add a new input, set the new input's value to Option 2, then add
      another input. Note that the value of the first input does not reset to Option 1.")
  )
))

server.R

库(闪亮)

代码语言:javascript
复制
shinyServer(function(input, output) {

  output$allInputs <- renderUI({
    # Get value of button, which represents number of times pressed (i.e. number of inputs added)
    inputsToShow <- input$appendInput
    # Return if button not pressed yet
    if(is.null(inputsToShow) || inputsToShow < 1) return()
    # Initialize list of inputs
    inputTagList <- tagList()
    # Populate the list of inputs
    lapply(1:inputsToShow,function(i){
      # Define unique input id and label
      newInputId <- paste0("input", i)
      newInputLabel <- paste("Input", i)
      # Prevent dynamic inputs from resetting
      newInputValue <- "Option 1"
      if (newInputId %in% names(input)) {
        newInputValue <- input[[newInputId]]
      }
      # Define new input
      newInput <- selectInput(newInputId, newInputLabel, c("Option 1", "Option 2", "Option 3"), selected=newInputValue)
      # Append new input to list of existing inputs
      inputTagList <<- tagAppendChild(inputTagList, newInput)
    })
    # Return updated list of inputs
    inputTagList
  })

})

(该解决方案在Nick's hints in the original gist from where you got the code of the promising solution上进行了指导)

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38083286

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档