0

I have a problem in regards to the post: Collect all input labels in Shiny R.

I have created a simple app based on the code from above mentioned post. So far I am able to select all the variables in a dataset and automatically create an UI consisting of input widgets based on the variables in the dataset. I am also able to display all the selected inputs in a table below. The whole idea is to create an application that will work the same when a different dataset is uploaded. (see code below).

The problem arise when I add a new object which gets stored as a input value. For example if I create an action-button. If I do this, the ID of the action-button will get displayed in the display-table and I don't want this.

If you don't understand what I mean, try to run the code below and notice in the display-table that one of the objects displayed is the "savebutton", which is the ID of the action-button on the page. I only want to display the input values from the variables in a given dataset that is uploaded to the app (in this case, it would be the variables in the dataset iris, but it should work on other datasets as well).

How can I modify my code to only show the input values from the variables in the dataset?

library(shiny)
library(tidyverse)
library(readxl)
library(shinythemes)
library(data.table)
library(RCurl)
library(randomForest)

# Read data
DATA <- datasets::iris


ui <- fluidPage(
  h3("Input-widgets:"),
  uiOutput("select"),
  
  hr(),
  
  h3("display-table:"),
  br(),
  tableOutput('show_inputs'),
  
  hr(),
  
  h3("Save button"),
  actionButton("savebutton", label = "Save", icon("save")),
  br(),
  "The button has no function right now other than to show the problem in the display-table"
  
) # End UI bracket



server <- function(input, output, session) {
  
# Create input widgets from dataset  
  output$select <- renderUI({
    df <- req(DATA)
    tagList(map(
      names(df[-1]),
      ~ ifelse(is.numeric(df[[.]]),
               yes = tagList(sliderInput(
                 inputId = paste0(.),
                 label = .,
                 value = mean(df[[.]], na.rm = TRUE),
                 min = round(min(df[[.]], na.rm = TRUE),2),
                 max = round(max(df[[.]], na.rm = TRUE),2)
               )),
               no = tagList(selectInput(
                 inputId = paste0(.),
                 label = .,
                 choices = sort(unique(df[[.]])),
                 selected = sort(unique(df[[.]]))[1],
               ))
      )
    ))
  })
  
# Display the input selections 
  AllInputs <- reactive({
    myvalues <- NULL
    for(i in 1:length(names(input))){
      myvalues <- as.data.frame(rbind(myvalues,(cbind(names(input)[i],input[[names(input)[i]]]))))
    }
    names(myvalues) <- c("Variable","Selected Value")
    myvalues
  })
  
  output$show_inputs <- renderTable({
    AllInputs()
})
  
  
} # End server bracket

shinyApp(ui, server)
Sound
  • 85
  • 1
  • 7

2 Answers2

3

If you use naming convention such as prefixing all input ids you generate with for example prefix_ then the following code will return what you need.

We need to transform input which is a disguised reactiveValues into a standard list before subsetting as input["name"] is not allowed.

AllInputs <- reactive({
  id_inputs <- grep("^prefix_", names(input), value=TRUE)
  
  if(length(id_inputs))
    data.frame(
      Variable = id_inputs,  
      "Selected Value" = unlist(reactiveValuesToList(input)[id_inputs])
    )
})

If you want to strip prefix in returned result then replace Variable=id_inputs with Variable=sub("^prefix_", "", id_inputs)

Billy34
  • 1,777
  • 11
  • 11
  • I'm having some troubles naming the "Variable" according to the names in "id_inputs". It returns the placement number in the id_inputs instead, and a normal "names()" function doesn't work for this. How do I return the names of the variables instead? – Sound Nov 04 '20 at 08:28
  • My bad. Forgot to ask for values instead of position in grep. I updated my answer – Billy34 Nov 04 '20 at 08:56
1

This could be achieved by adjusting your reactive AllInputs like so:

  1. I add a vector id_exclude of input ids which should be excluded from the table
  2. I add a vector id_include as the difference between all inputs and the ids to exclude
  3. I also added an if condition to check if id_include contains any elements because otherwise one gets an error when starting the app.
      AllInputs <- reactive({
        id_exclude <- c("savebutton")
        
        id_include <- setdiff(names(input), id_exclude)
        
        if (length(id_include) > 0) {
          myvalues <- NULL
          for(i in id_include) {
            myvalues <- as.data.frame(rbind(myvalues, cbind(i, input[[i]])))
          }
          names(myvalues) <- c("Variable", "Selected Value")
          myvalues
        }
      })
stefan
  • 90,330
  • 6
  • 25
  • 51
  • Thanks for the answer! Is there a way to rearrange the display table so that the variables selected displays in the same order as the input widgets? I think the table would be more intuitive to read that way. – Sound Nov 04 '20 at 13:11