Обновление фрейма данных при нажатии в DT

#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.