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:
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.
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)
}