Вопрос или проблема
Данный код:
library(shiny)
library(DT)
# Определить интерфейс
ui <- fluidPage(titlePanel("Страница тестирования"),
DTOutput("strings"))
# Определить логику сервера
server <- function(input, output, session) {
string_data <- data.frame(
Name = c("ALICE", "BOB", "CHARLIE", "STEVE", "JENNIFER"),
City = c("Нью-Йорк", "Лос-Анджелес", "Чикаго", "Синсиннати", "Кливленд"),
Occupation = c("Инженер", "Художник", "Доктор", "Адвокат", "Бухгалтер")
)
table_data <- reactiveVal(string_data)
temp_table <- tibble(string_data)
# Отобразить таблицу
output$strings <- renderDT({
datatable(
table_data(),
editable = TRUE,
options = list(
pageLength = 5
)
)
})
data_proxy <- dataTableProxy("strings")
# Наблюдать за изменениями ячеек
observeEvent(input$strings_cell_edit, {
new_edit <- input$strings_cell_edit
commit_edits <- FALSE
if (!is.null(new_edit)) {
if (new_edit$col == 1) {
check_me <- feat_name_val(new_edit$value, string_data)
if (check_me == TRUE) {
commit_edits = TRUE
}
}
if (commit_edits == TRUE) {
# Обновите temp_table с новым значением
temp_table[new_edit$row, new_edit$col] <- new_edit$value
print("отладка")
} else if (commit_edits == FALSE) {
replaceData(data_proxy, temp_table, resetPaging = FALSE)
reloadData(data_proxy)
new_edit <- NULL
#popup_message(check_return)
}
}
})
}
feat_name_val = function(name,source_df) {
acceptable_characters = list("A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
"K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
"U", "V", "W", "X", "Y", "Z", "_", "0", "1", "2",
"3", "4", "5", "6", "7", "8", "9")
name_split = strsplit(name, "")[[1]]
dupl_check = name %in% source_df$Name
true_check = "TRUE" %in% dupl_check
if (length(name_split) > 27) {
return ('Это поле должно содержать 27 символов или меньше')
}
if (length(name_split) <= 0) {
return ('Это обязательное поле')
}
if (true_check == "TRUE") {
return('Это имя уже используется.')
}
count = 0
for (char in name_split) {
if (char %in% acceptable_characters) {
count = count + 1
}
}
if (length(name_split) == count) {
return (TRUE)
} else {
return ('Это поле может содержать только заглавные буквенно-цифровые символы, разделенные нижними подчеркиваниями')
}
}
# Запустить приложение
shinyApp(ui = ui, server = server)
Если я дважды щелкаю на ячейке, чтобы изменить ее на допустимое значение (все заглавные буквы), а затем щелкаю за пределами ячейки, я могу видеть, что отредактированное значение правильно сохраняется в temp_table.
Если я затем дважды щелкаю на другой ячейке и вношу в нее допустимое редактирование, после того как я щелкаю за пределами ячейки, чтобы “подтвердить” редактирование, temp_table удаляет предыдущее редактирование и сохраняет только самое недавно отредактированное значение.
Фактический интерфейс приложения по-прежнему будет показывать отредактированную ячейку, но за кулисами отредактированное значение в temp_table будет удалено.
Цель здесь – иметь таблицу, которая отслеживает все отредактированные ячейки, чтобы при внесении недопустимого редактирования я мог перезагрузить таблицу, используя следующие строки:
replaceData(data_proxy, temp_table, resetPaging = FALSE)
reloadData(data_proxy)
Таким образом, недопустимая запись визуально исчезает, но допустимые записи сохраняются.
Я попробовал следующую строку кода:
temp_table[new_edit$row, new_edit$col] <- new_edit$value
Идея заключается в том, что каждый раз, когда ячейка редактируется, изменение записывается во временную таблицу, которая служит буфером, так что, если мне нужно сделать перезагрузку, я могу сохранить редактирования.
Однако в отладчике, если я ставлю точку останова после этой строки, я вижу, что при втором или третьем редактировании предыдущие изменения больше не присутствуют в этой таблице, несмотря на то, что она не была переинициализирована. Есть ли какое-то поведение DT, которое я не понимаю здесь?
Ответ или решение
Вопрос о том, почему в R Shiny при редактировании ячейки в дата-таблице теряются изменения, является актуальным для многих разработчиков, использующих эту библиотеку для создания интерактивных приложений. Углубляясь в предоставленный вами код и наблюдая за поведением приложения, можно выделить несколько ключевых причин, вызванных особенностями работы с DT
и reactiveVal
, которые могут привести к потере изменений.
Анализ проблемы
Ваш код создает интерактивную таблицу с возможностью редактирования. При каждом редактировании ячейки значение, как ожидается, должно обновляться в temp_table
, которая служит временным хранилищем для отслеживания изменений. Однако, как вы упомянули, при редактировании нескольких ячеек изменения могут теряться, и temp_table
отражает только последнее редактирование.
Причиной этого поведения является то, как вы используете temp_table
. В вашем коде вы определяете temp_table
как tibble
на основе исходного string_data
, но не обновляете ее значениями из table_data()
, когда происходит редактирование. Это значит, что temp_table
всегда фактически инициализируется с первоначальными данными, а обновления не сохраняются должным образом.
Подходы к решению проблемы
-
Обновление
temp_table
при каждой операции редактирования: Когда происходит редактирование, необходимо всегда загружать текущее состояние данных. Это можно сделать, инициализировавtemp_table
с помощьюtable_data()
в момент, когда эти данные обновляются. -
Использование
reactiveVal
для временной таблицы: Вместо использования обычного тибли тогда, когда данные редактируются, целесообразно использоватьreactiveVal
, что гарантирует отслеживание изменений в состоянии. Примерно так:temp_table <- reactiveVal(data.frame(string_data))
Затем при каждом редактировании:
if (commit_edits == TRUE) { current_data <- temp_table() current_data[new_edit$row, new_edit$col] <- new_edit$value temp_table(current_data) # Обновляем временную таблицу }
-
Обеспечение сохранности изменений: Перед вызовом
replaceData(data_proxy, temp_table, ...)
убедитесь, чтоtemp_table
всегда содержит актуальные данные. Это позволит откатиться к предыдущему состоянию, если последующие изменения будут недействительными.
Заключение
Таким образом, для устранения проблемы с потерей изменений между редактированиями ячеек в R Shiny в таблице DT
, разработчики должны применять подходы, которые обеспечивают актуальность данных во временном хранилище. Обновление temp_table
каждый раз при редактировании с использованием reactiveVal
поможет сохранить все предыдущие изменения. Такой способ работы с данными позволит без проблем отслеживать все изменения и аккуратно реагировать на недопустимые вводы, восстанавливая данные в соответствии с необходимыми условиями.