Почему таблица R Shiny теряет изменения между редактированиями ячеек?

Вопрос или проблема

Данный код:

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 всегда фактически инициализируется с первоначальными данными, а обновления не сохраняются должным образом.

Подходы к решению проблемы

  1. Обновление temp_table при каждой операции редактирования: Когда происходит редактирование, необходимо всегда загружать текущее состояние данных. Это можно сделать, инициализировав temp_table с помощью table_data() в момент, когда эти данные обновляются.

  2. Использование 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)  # Обновляем временную таблицу
    }
  3. Обеспечение сохранности изменений: Перед вызовом replaceData(data_proxy, temp_table, ...) убедитесь, что temp_table всегда содержит актуальные данные. Это позволит откатиться к предыдущему состоянию, если последующие изменения будут недействительными.

Заключение

Таким образом, для устранения проблемы с потерей изменений между редактированиями ячеек в R Shiny в таблице DT, разработчики должны применять подходы, которые обеспечивают актуальность данных во временном хранилище. Обновление temp_table каждый раз при редактировании с использованием reactiveVal поможет сохранить все предыдущие изменения. Такой способ работы с данными позволит без проблем отслеживать все изменения и аккуратно реагировать на недопустимые вводы, восстанавливая данные в соответствии с необходимыми условиями.

Оцените материал
Добавить комментарий

Капча загружается...