Search code examples
rvalidationshinyreactivedt

Reactive update cells fomart in DT shiny


What I am trying to do is to have a DT in shiny that highlights the cells that do not meet specific rules (using the validate package) from a file the user can upload, so the user can edit the cells to values that meet the criteria, and if the new value is correct, the highlighted cell should not be longer highlighted.

In the code below, I am able to highlight the cells that does not meet the criteria, but I am not able to update the highlights once the user edit the cells. I know this is something related to the fact I am calling the submitted file when I validate the data, but I do not know how I can access the edited data in DT, so the rules can be run in a reactive way by the user input.

I would prefer to change the highlight each time the user edits the cell, but I do not mind if this can be better implemented using a validation button for example.

Here is a minimum reproducible example of what I have so far. Please note, I use a submit file button to upload the file, but the excel file I am using for this example can be easily created by:

df_submitted <- data.frame(x=c(1:20),y=c(0:1),z=c("R"))
df_submitted[[2,2]] <- 3
df_submitted[[3,3]] <- "python"

Shiny app:

library(shiny)
library(readxl)
library(openxlsx)
library(tidyverse)
library(validate)
library(DT)

ui <- (fluidPage(
  titlePanel("Test"),
  sidebarLayout(sidebarPanel(
    fileInput("df_submitted","Upload your file",accept = c(".xlsx"))
  ),
  mainPanel(
    DTOutput("df_tested"))
  )
))

server <- function(input, output, session) {
  df <- reactiveValues(data=NULL)
  
  #Upload file
  df_uploaded <- reactive({  
    file_submitted <- input$df_submitted
    file_ext <- tools::file_ext(file_submitted$name)
    file_path <- file_submitted$datapath

    if (is.null(file_submitted)){
      return(NULL)
    }
    if (file_ext=="xlsx"){
      read_xlsx(file_path,sheet=1)
    }
  })

  observe({
    df$data <- df_uploaded()
  })
  
  ###Validate form
  validator_react <- reactive({
    req(df$data)
    df_validate <- df$data
    ##rules
    rules <- validator(
      x>5,
      y<2,
      z=="R"
    )
    #Confront rules against df
    out <- confront(df_validate,rules)
    cells_dt <- data.frame(values(out))
    cells_dt <- cells_dt %>%
      mutate_all(function(x) ifelse(x==TRUE,0,1))
    #Join cells that fail the rules for future highlight in DT
    df_validate <- cbind(df_validate,cells_dt)
    df_validate
  })
  
  output$df_tested=renderDT({
    df_dt <- validator_react()
    visible_cols <- 1:((ncol(df_dt)/2))
    hidden_cols <- ((ncol(df_dt)/2)+1):ncol(df_dt)
    
    df_dt %>%
      datatable(
        editable=T,
        options=list(
          dom="Bfrtip",
          autoWidth=T,
          columnDefs=list(list(targets=hidden_cols,visible=F)))) %>%
      formatStyle(visible_cols,hidden_cols,
                  backgroundColor=styleEqual(c(0,1),c("white","#FFC7CE")),
                  color=styleEqual(c(0,1),c("black","#9C0006")))
  },server=F)
  
 #The below code is not working, I saw some examples using a similar approach but, not sure how to implemented, but I guess the solution goes in this direction
  dt_proxy <- dataTableProxy("df_tested")
  observeEvent(input$update_cells, {
    info <- input$update_cells
    df$data <<- editData(df$data,info,dt_proxy)
  }) 
#

}#End server

shinyApp(ui = ui, server = server)

Solution

  • Try this

    library(shiny)
    library(readxl)
    library(openxlsx)
    library(tidyverse)
    library(validate)
    library(DT)
    
    ui <- (fluidPage(
      titlePanel("Test"),
      sidebarLayout(sidebarPanel(
        fileInput("df_submitted","Upload your file",accept = c(".xlsx"))
      ),
      mainPanel(
        DTOutput("df_tested"))
      )
    ))
    
    server <- function(input, output, session) {
      df <- reactiveValues(data=NULL)
    
      #Upload file
      df_uploaded <- reactive({
        file_submitted <- input$df_submitted
        file_ext <- tools::file_ext(file_submitted$name)
        file_path <- file_submitted$datapath
    
        if (is.null(file_submitted)){
          return(NULL)
        }
        if (file_ext=="xlsx"){
          read_xlsx(file_path,sheet=1)
        }
      })
    
      observe({
        df$data <- df_uploaded()
      })
    
      ###Validate form
      validator_react <- reactive({
        req(df$data)
        df_validate <- df$data
        ##rules
        rules <- validator(
          x>5,
          y<2,
          z=="R"
        )
        #Confront rules against df
        out <- confront(df_validate,rules)
        cells_dt <- data.frame(values(out))
        cells_dt <- cells_dt %>%
          mutate_all(function(x) ifelse(x==TRUE,0,1))
        #Join cells that fail the rules for future highlight in DT
        df_validate <- cbind(df_validate,cells_dt)
        df_validate
      })
    
      output$df_tested=renderDT({
        df_dt <- validator_react()
        visible_cols <- 1:((ncol(df_dt)/2))
        hidden_cols <- ((ncol(df_dt)/2)+1):ncol(df_dt)
    
        df_dt %>%
          datatable(
            editable=T,
            options=list(
              dom="Bfrtip",
              autoWidth=T,
              columnDefs=list(list(targets=hidden_cols,visible=F)))) %>%
          formatStyle(visible_cols,hidden_cols,
                      backgroundColor=styleEqual(c(0,1),c("white","#FFC7CE")),
                      color=styleEqual(c(0,1),c("black","#9C0006")))
      },server=F)
    
      #The below code is not working, I saw some examples using a similar approach but, not sure how to implemented, but I guess the solution goes in this direction
      dt_proxy <- dataTableProxy("df_tested")
      observeEvent(input$df_tested_cell_edit, {
        info <- input$df_tested_cell_edit
        df$data <<- editData(df$data,info,dt_proxy)
      })
    
    }#End server
    
    shinyApp(ui = ui, server = server)