Search code examples
rshinyshinyappsrshiny

how to dynamically update the column names in choices of SelectInput in R shiny


I'm working on a shiny app where I have 'SplitColumn' (to split the merged columns), 'Replace Values', and 'Remove Column'. All of these functions are dependent on the column selection from'selectInput' in the options (column names).

Whenever I use 'SplitColumn,' it creates additional columns such as 'Unmerged Type1' and 'Unmerged Type2,'in the datatable as expected but these new columns are not dynamically updated in the 'SelectInput' in the choices columns to select, the same issue is still there while using other buttons too.

Could someone help me to solve this issue.

csv data

ID  Type   Range
21  A1 B1   100
22  C1 D1   200
23  E1 F1   300

app.R

library(shiny)
library(reshape2)
#source('splitColumn_stack.R')
library(DT)
library(tibble)


###function for deleting the rows
splitColumn <- function(data, column_name) {
  newColNames <- c("Unmerged_type1", "Unmerged_type2")
  newCols <- colsplit(data[[column_name]], " ", newColNames)
  after_merge <- cbind(data, newCols)
  after_merge[[column_name]] <- NULL
  after_merge
}
###_______________________________________________
### function for inserting a new column

fillvalues <- function(data, values, columName){
  df_fill <- data
  vec <- strsplit(values, ",")[[1]]
  df_fill <- tibble::add_column(df_fill, newcolumn = vec, .after = columName)
  df_fill
}

##function for removing the colum

removecolumn <- function(df, nameofthecolumn){
  df[ , -which(names(df) %in% nameofthecolumn)]
}

### use a_splitme.csv for testing this program

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      fileInput("file1", "Choose CSV File", accept = ".csv"),
      checkboxInput("header", "Header", TRUE),
      actionButton("Splitcolumn", "SplitColumn"),
      selectInput(inputId='selectcolumn', label='select column', ''),
      actionButton("deleteRows", "Delete Rows"),
      textInput("textbox", label="Input the value to replace:"),
      actionButton("replacevalues", label = 'Replace values'),
      actionButton("removecolumn", "Remove Column")
    ),
    mainPanel(
      DTOutput("table1")
    )
  )
)

server <- function(session, input, output) {
  rv <- reactiveValues(data = NULL)
  
  observeEvent(input$file1, {
    file <- input$file1
    ext <- tools::file_ext(file$datapath)
    
    req(file)
    
    validate(need(ext == "csv", "Please upload a csv file"))
    
    rv$data <- read.csv(file$datapath, header = input$header)
    
    updateSelectInput(session, 'selectcolumn', 'select column', names(rv$data))
    
  })
  
  observeEvent(input$Splitcolumn, {
    rv$data <- splitColumn(rv$data, input$selectcolumn)
  })
  
  observeEvent(input$deleteRows,{
    if (!is.null(input$table1_rows_selected)) {
      rv$data <- rv$data[-as.numeric(input$table1_rows_selected),]
    }
  })
  
  output$table1 <- renderDT({
    rv$data
  })
  observeEvent(input$replacevalues, {
    rv$data <- fillvalues(rv$data, input$textbox, input$selectcolumn)
  })
  observeEvent(input$removecolumn, {
    rv$data <- removecolumn(rv$data,input$selectcolumn)
  })
}

shinyApp(ui, server)

Solution

  • I only minimally changed your file, but hopefully this does what you are looking for. Rather than selectInput, I used uiOutput and placed a selectInput within it. The choices in this new selectInput directly use the reactivevalues. Any time rv$data changes, it updates the choices. Because of this change there was no need for the updateSelectInput so I removed that as well. I believe a key reason your code didn't work as you wanted is because you only used updateSelectInput in one place, the file upload. Meaning whenever you delete or split a column, you update the reactiveValue, but it never updated the choices since the reactiveValue itself wasn't tied to the selectInput. Hope that makes sense!

    library(shiny)
    library(reshape2)
    library(DT)
    library(tibble)
    
    
    ###function for deleting the rows
    splitColumn <- function(data, column_name) {
      newColNames <- c("Unmerged_type1", "Unmerged_type2")
      newCols <- colsplit(data[[column_name]], " ", newColNames)
      after_merge <- cbind(data, newCols)
      after_merge[[column_name]] <- NULL
      after_merge
    }
    ###_______________________________________________
    ### function for inserting a new column
    
    fillvalues <- function(data, values, columName){
      df_fill <- data
      vec <- strsplit(values, ",")[[1]]
      df_fill <- tibble::add_column(df_fill, newcolumn = vec, .after = columName)
      df_fill
    }
    
    ##function for removing the colum
    
    removecolumn <- function(df, nameofthecolumn){
      df[ , -which(names(df) %in% nameofthecolumn)]
    }
    
    ### use a_splitme.csv for testing this program
    
    ui <- fluidPage(
      sidebarLayout(
        sidebarPanel(
          fileInput("file1", "Choose CSV File", accept = ".csv"),
          checkboxInput("header", "Header", TRUE),
          actionButton("Splitcolumn", "SplitColumn"),
          uiOutput("selectUI"),
          actionButton("deleteRows", "Delete Rows"),
          textInput("textbox", label="Input the value to replace:"),
          actionButton("replacevalues", label = 'Replace values'),
          actionButton("removecolumn", "Remove Column")
        ),
        mainPanel(
          DTOutput("table1")
        )
      )
    )
    
    server <- function(session, input, output) {
      rv <- reactiveValues(data = NULL)
      
      observeEvent(input$file1, {
        file <- input$file1
        ext <- tools::file_ext(file$datapath)
        
        req(file)
        
        validate(need(ext == "csv", "Please upload a csv file"))
        
        rv$data <- read.csv(file$datapath, header = input$header)
        
      })
      
      output$selectUI<-renderUI({
        req(rv$data)
        selectInput(inputId='selectcolumn', label='select column', choices = names(rv$data))
      })
      
      
      observeEvent(input$Splitcolumn, {
        rv$data <- splitColumn(rv$data, input$selectcolumn)
      })
      
      observeEvent(input$deleteRows,{
        if (!is.null(input$table1_rows_selected)) {
          rv$data <- rv$data[-as.numeric(input$table1_rows_selected),]
        }
      })
      
      output$table1 <- renderDT({
        rv$data
      })
      observeEvent(input$replacevalues, {
        rv$data <- fillvalues(rv$data, input$textbox, input$selectcolumn)
      })
      observeEvent(input$removecolumn, {
        rv$data <- removecolumn(rv$data,input$selectcolumn)
      })
    }
    
    shinyApp(ui, server)