#r #shiny #reactive
Вопрос:
У моего data.frame
есть один логический столбец. Я хочу отобразить данные с DT::renderDataTable
помощью , и я хотел бы изменить значение в логическом столбце, когда была нажата строка.
Я нашел рабочий пример, но я стараюсь использовать reactive
вместо reactiveValues/observeEvent
этого .
Теперь отображается DT, который выполняет некоторую обработку при нажатии на строку, однако значения не меняются. Вот пример:
library(shiny)
library(DT)
ui <- shiny::fluidPage(
DT::dataTableOutput("myTable")
)
server <- function(input, output, session) {
tableData = reactive({
ans <- data.frame(
Name = LETTERS[1:6],
YesNo= rep(c(TRUE,FALSE), each=3),
stringsAsFactors=FALSE
)
# transform T/F to checkbox symbol / NA
ans$YesNo <- ifelse(ans$YesNo,
as.character(shiny::icon("ok", lib = "glyphicon")), NA
)
# handle click on row
if(!is.null(input$myTable_rows_selected)) {
r_idx <- input$myTable_rows_selected[1]
message("r_idx: ", r_idx)
ans[r_idx, "YesNo"] <- ifelse(is.na(ans[r_idx, "YesNo"]),
as.character(shiny::icon("ok", lib = "glyphicon")), NA
)
#Send proxy (no need to refresh whole table)
DT::replaceData(DT::dataTableProxy('myTable'), ans)
}
ans
})
output$myTable = DT::renderDataTable({tableData()},
selection = list(mode = "single", target = 'row'),
style = "bootstrap",
rownames = FALSE,
colnames = c("Letter", "Yes?"),
options = list(lengthMenu = c(5, 10, 25, 50, 100), pageLength = 5),
escape=FALSE # do not show html code
)
}
shinyApp(ui, server)
Я застрял. Любой намек будет оценен по достоинству.
Комментарии:
1. Я не думаю
reactive
, что это хороший подход к решению этой проблемы. 1. Использование reactive предназначено для хранения некоторых данных и может быть использовано в других местах, как это сделали выtableData
. Однако, еслиreplaceData
уreactive
вас есть проблема , это не рекомендуется. Вам лучше сделать это внутри наблюдателя, не реагирующего. 2. Ключевой момент этой проблемы заключается в том, что вам нужен контейнер для хранения текущего состояния df и постоянного обновления его по щелчку, поэтому внутри реактивного вы не можете использоватьans
, это начальное состояние, а не текущее состояние. Вам нужно использоватьtableData
.2. Это создает парадокс, вы не можете назвать реактивным само по себе внутри себя. 3. Еще одна проблема, для вас
renderDataTable
, вы не хотите звонитьtableData
, только звонитеans
. Поскольку вы визуализируете его только один раз в исходном состоянии, будут использоваться более поздние обновленияreplacedata
.reactiveVal
иreactiveValues
это лучшее решение, которое я могу придумать здесь, другое возможное решение-назначить ваш df глобальным, чтобы обновить его (<3. Спасибо за эти полезные объяснения, @lz100. Если вы преобразуете их в ответ, я приму это.
Ответ №1:
Я не хочу повторяться, поэтому я просто оставляю комментарии там. Вот еще один подход к глобальному назначению <<-
. Так что вам не нужно использовать reactiveValues
или reactive
или reactiveVal
.
library(shiny)
library(DT)
ui <- shiny::fluidPage(
DT::dataTableOutput("myTable")
)
server <- function(input, output, session) {
ans <<- data.frame(
Name = LETTERS[1:6],
YesNo= rep(c(TRUE,FALSE), each=3),
stringsAsFactors=FALSE
)
ans$YesNo <<- ifelse(ans$YesNo,
as.character(shiny::icon("ok", lib = "glyphicon")), NA
)
proxy <- dataTableProxy('myTable')
observeEvent(input$myTable_rows_selected, {
req(input$myTable_rows_selected)
r_idx <- input$myTable_rows_selected[1]
ans$YesNo[r_idx] <<- ifelse(is.na(ans$YesNo[r_idx]), as.character(shiny::icon("ok", lib = "glyphicon")), NA)
DT::replaceData(dataTableProxy('myTable'), data = ans)
})
output$myTable = DT::renderDataTable({ans},
selection = list(mode = "single", target = 'row'),
style = "bootstrap",
# rownames = FALSE,
colnames = c("Letter", "Yes?"),
options = list(lengthMenu = c(5, 10, 25, 50, 100), pageLength = 5),
escape=FALSE # do not show html code
)
}
shinyApp(ui, server)
Я прокомментировал rownames = FALSE
это . Если это включено, обновление не будет работать. Я не знаю почему, чувствую себя как жучок из DT.