Home > Net >  R Shiny App - selectizeInput choices from data columns not reactive to new data
R Shiny App - selectizeInput choices from data columns not reactive to new data

Time:11-22

I am trying to modularize a functioning shiny app where a user uploads a file and then selects columns from the uploaded data into different roles. This worked fine before when the app had all the reactive functionality in a single file, but now with the selectizeInputs created in a module, the choices for the selectizeInputs are not reactive to changes to the uploaded data.

Here is a minimal working example. Running the app, let's say I upload a csv file with column headers "A" (numeric), "B", and "C". I select A and B into 'y' and 'x' respectively as shown in the image below: File 1 upload with columns A and B selected

Now, without refreshing the app, the user uploads a different csv file with column headers "D" (numeric), "E", and "F". The 'y' and 'x' inputs are still set to the columns from the previous file and the choices still reflect the columns from the previous file as well.

File2 is uploaded but previous choices persist.

Any pointers on how to get the selectizeInput choices to react to the dataframe change would be extremely helpful! Thanks. Here is the code for this example.

First, a function to get numeric columns only:

#Select numeric columns of data frame
getNumCols = function(df){
  numCols = unlist(lapply(df, is.numeric))
  colnames(df)[numCols]
}

Input module UI function:


#inputs module Input function
inputsUI <- function(id){
  ns <- NS(id)
  
  tagList(
    uiOutput(ns('y')),
    uiOutput(ns('x'))
  )
}

Input module server function:

inputsServer <- function(id, data){
  moduleServer(
    id,
    function(input, output, session){
      ns <- session$ns
      
      numCols <- reactive({getNumCols(data)})
      cols <- reactive({colnames(data)})
      
      #Create Response Selection
      output$y = renderUI({
        selectizeInput(
          ns('y'), "Y variable", choices = numCols(), multiple = FALSE
        )
      })
      
      #Create Part Selection
      output$x = renderUI({
        selectizeInput(
          ns('x'), "X variable", choices = cols(), multiple = FALSE
        )
      })
    }
  )
}

And here is the code for the rest of the app:

# Define UI for application that draws a histogram
ui <- fluidPage(

    # Application title
    titlePanel("Title"),

    # Sidebar with a slider input for number of bins 
    sidebarLayout(
        sidebarPanel(
          fileInput("file", "Upload a .csv file", accept = ".csv") 
        ),

        # Show a plot of the generated distribution
        mainPanel(
           inputsUI("inputs")
        )
    )
)

# Define server logic required to draw a histogram
server <- function(input, output) {
  userFile <- reactive({
    validate(need(input$file, message=FALSE))
    input$file
  })
  
  getData <- reactive({
    read.csv(userFile()$datapath)
  })
  
  inputsServer("inputs", getData())
}

# Run the application 
shinyApp(ui = ui, server = server)

CodePudding user response:

The issue is that you pass the dataframe stored in the reactive data to the module server instead of passing the reactive itself. Hence, to fix your issue pass the reactive to your server, i.e. do inputsServer("inputs", getData) and replace data in the module server by data(). Doing so the module server will take a reactive dependency on getData and will update the selectizeInput when getData changes:

Module server:

inputsServer <- function(id, data) {
  moduleServer(
    id,
    function(input, output, session) {
      ns <- session$ns

      numCols <- reactive({
        getNumCols(data())
      })
      cols <- reactive({
        colnames(data())
      })
      output$y <- renderUI({
        selectizeInput(
          ns("y"), "Y variable",
          choices = numCols(), multiple = FALSE
        )
      })
      output$x <- renderUI({
        selectizeInput(
          ns("x"), "X variable",
          choices = cols(), multiple = FALSE
        )
      })
    }
  )
}

Server:

server <- function(input, output) {
  userFile <- reactive({
    validate(need(input$file, message = FALSE))
    input$file
  })

  getData <- reactive({
    read.csv(userFile()$datapath)
  })

  inputsServer("inputs", getData)
}
  • Related