У меня есть приложение Shiny, которое позволяет пользователю загружать CSV-файл, а затем отображать данные. Затем у меня есть несколько пользовательских функций для создания различных элементов пользовательского интерфейса, которые передаются на сервер, например:
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)
}
})
}
и на сервере у меня есть:
output$GroupBy_Area <- GroupByUI(
id = "area",
data = dataFlt()
)
и это работает, когда я загружаю первый CSV (например, Group By selectInput обновляется с правильными именами переменных).
Для контекста dataFlt() — это reactive, для него установлено значение NULL и оно обновляется каждый раз, когда я загружаю новый/другой CSV.
dataFlt <- reactive({
req(uploadedData$data)
datafl <- uploadedData$data
})
У меня проблема в том, что если я загрузил CSV, нарисовал данные, а затем изменил загружаемый CSV (например, я изменил данные, которые хочу построить, имена переменных меняются), параметры в группе «Группировать по» selectInput не обновляйте соответствующим образом, ЕСЛИ я не перенесу renderUI непосредственно на сервер:
output$GroupBy_Area <- renderUI({
req(dataFlt())
GroupByUI(id = "area",
data = dataFlt())
})
и я удаляю renderUI из функции:
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)
}
}
Благодаря этому последнему коду (переносу renderUI непосредственно на сервер) он работает каждый раз, когда я меняю загружаемые данные.
Чего я не понимаю, так это почему. Какая разница? И почему оригинальный код работает с первого раза?





Очень простое решение — передать в функцию GroubByUI сам реактивный объект, а не его значение:
output$GroupBy_Area <- GroupByUI(
id = "area",
data = dataFlt
)
И измените функцию, чтобы «вызвать» реактив и получить ее значение:
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)
}
})
}
Таким образом, функция renderUI зависит от реактива и должна соответствующим образом обновляться.
Нереактивный код в функции сервера на самом деле запускается только один раз, в начале сеанса. Чтобы такие объекты, как renderUI, наблюдатели и т. д., обновлялись правильно, они должны иметь прямую зависимость от самих реактивов.
Они получают эту зависимость, если реактивный объект вызывается непосредственно внутри них, но в вашем коде dataFlt() вызывается вашей функцией, которая не работает в реактивном контексте, а не функцией renderUI, которая тогда не принимает зависимости на нем и не обновляется, когда вы считаете, что это необходимо.
Надеюсь, это поможет :)
Попробуйте вызвать свой первый
GroupByUI()вobserve()r, чтобы обеспечить ему реактивный контекст.