I have a Shiny app that allows the user to load a csv and then plot the data. I then have some custom functions to create different UI elements, that are passed into the server, for example:
GroupByUI <- function(id,
data,
label = 'Group By',
selected = 'All',
None = FALSE,
All = TRUE) {
renderUI({
xsecs <- data %>%
select_if(is.character) %>%
names()
if (None) {
xsecs <- c('None', xsecs)
}
if (All == FALSE) {
xsecs <- xsecs[-which(xsecs == 'All')]
selectInput(inputId = paste0('groupby_', id),
label = label,
choices = as.list(xsecs),
selected = as.list(xsecs)[1])
} else {
selectInput(inputId = paste0('groupby_', id),
label = label,
choices = as.list(xsecs),
selected = selected)
}
})
}
and in the server I have:
output$GroupBy_Area <- GroupByUI(
id = "area",
data = dataFlt()
)
and this works when I load a first csv (e.g. the Group By selectInput
is updated with the correct variable names).
For context, dataFlt() is a reactive
and it's set to NULL and updates every time I load a new/different csv.
dataFlt <- reactive({
req(uploadedData$data)
datafl <- uploadedData$data
})
The problem that I have is, that if I have loaded a csv, plotted the data and then I change the csv that I load (e.g. I change the data I want to plot, variables' names change), the options in the Group By selectInput
do not update accordingly UNLESS I move the renderUI
directly to the server:
output$GroupBy_Area <- renderUI({
req(dataFlt())
GroupByUI(id = "area",
data = dataFlt())
})
and I remove the renderUI
from the function:
GroupByUI <- function(id, data, label = 'Group By', selected = 'All', None = FALSE, All = TRUE) {
xsecs <- data %>%
select_if(is.character) %>%
names()
if (None) {
xsecs <- c('None', xsecs)
}
if (All == FALSE) {
xsecs <- xsecs[-which(xsecs == 'All')]
selectInput(inputId = paste0('groupby_', id),
label = label,
choices = as.list(xsecs),
selected = as.list(xsecs)[1])
} else {
selectInput(inputId = paste0('groupby_', id),
label = label,
choices = as.list(xsecs),
selected = selected)
}
}
With this latest code (moving the renderUI
directly to the server), it works every time I change the data I load.
What I don't understand is why. What's the difference? And why do the original code works the first time?
A very simple solution is to pass the reactive object itself, and not its value, to the GroubByUI
function:
output$GroupBy_Area <- GroupByUI(
id = "area",
data = dataFlt
)
And modify the function to "call" the reactive and get its value:
GroupByUI <- function(id,
data,
label = 'Group By',
selected = 'All',
None = FALSE,
All = TRUE) {
renderUI({
xsecs <- data() %>%
select_if(is.character) %>%
names()
if (None) {
xsecs <- c('None', xsecs)
}
if (All == FALSE) {
xsecs <- xsecs[-which(xsecs == 'All')]
selectInput(inputId = paste0('groupby_', id),
label = label,
choices = as.list(xsecs),
selected = as.list(xsecs)[1])
} else {
selectInput(inputId = paste0('groupby_', id),
label = label,
choices = as.list(xsecs),
selected = selected)
}
})
}
This way, the renderUI
function takes a dependency on the reactive and should update accordingly.
The not-reactive code in the server function is actually only run once, at the start of the session. In order for objects like renderUI, observers etc to update correctly, they need to have a direct dependency on the reactive themselves.
They get this dependency if the reactive object is called directly inside them, but in your code, dataFlt()
is called by your function, which does not work in a reactive context, and not by the renderUI
function, that then doesn't take a dependency on it and doesn't update when you think it should.
I hope this helps :)