I am creating an app whom the goal is to compute rowmeans and coefficient variation (by rows). Each id got 3 measures. The user must be able to remove one or two values if the cv is too high (8).
I success to manipulate the tables like I expected, but cannot find the solution to removed the values of the cells while I click on it. Here is the current example of the app below.
library(shiny)
library(DT)
ui <- fluidPage(
fluidRow(
column(width = 8, DTOutput("table1")),
column(width = 4, DTOutput("table2"))
),
# Custom CSS to define the initial cell color
tags$head(
tags$style(HTML("
.default-cell-color {
background-color: lightgreen !important;
}
.selected-cell-color {
background-color: lightcoral !important;
}
"))
)
)
server <- function(input, output, session) {
# Sample data for demonstration purposes (contains only numeric values)
data <- data.frame(
ID = c(1, 2, 3),
Measure1 = c(25, 30, 22),
Measure2 = c(30, 32, 28),
Measure3 = c(35, 40, 30)
)
# Add a new column for row means
data$row_mean <- apply(data[, -1], 1, mean) # Calculate row means for numeric columns (excluding the first column)
# Add a new column for coefficient of variation (CV)
data$cv <- apply(data[, -1], 1, function(row) (sd(row) / mean(row)) * 100)
# Render the first table using DT::renderDataTable
output$table1 <- DT::renderDataTable({
datatable(
data[, c(1:4)],
selection = "none", # Disable row selection
editable = FALSE, # Disable cell editing
class = "cell-color-table",
options = list(
dom = "t", # Display table only without search bar, etc.
pageLength = 10, # Number of rows per page
columnDefs = list(
list(targets = "_all", className = "dt-center") # Center-align all columns
),
initComplete = JS(
"function(settings, json) {",
" var table = settings.oInstance.api();",
" table.cells().every(function() {",
" $(this.node()).addClass('default-cell-color');",
" });",
" table.on('click.dt', 'td', function() {",
" var cell = $(this);",
" var table = cell.closest('table').DataTable();",
" var row = table.cell(this).index().row;",
" var col = table.cell(this).index().column;",
" var isSelected = cell.hasClass('selected-cell-color');",
" if (isSelected) {",
" cell.removeClass('selected-cell-color');",
" cell.addClass('default-cell-color');",
" } else {",
" cell.removeClass('default-cell-color');",
" cell.addClass('selected-cell-color');",
" }",
" Shiny.setInputValue('selected_cells', {row: row, col: col, isSelected: !isSelected});",
" });",
"}"
)
)
)
})
# Render the second table for row means and CV
output$table2 <- DT::renderDataTable({
datatable(
data[, c("row_mean", "cv")],
selection = "none", # Disable row selection
editable = FALSE, # Disable cell editing
class = "cell-color-table",
options = list(
dom = "t", # Display table only without search bar, etc.
pageLength = 10, # Number of rows per page
columnDefs = list(
list(targets = "_all", className = "dt-center") # Center-align all columns
)
)
) %>%
formatStyle(
'cv',
backgroundColor = styleInterval(8, c('lightgreen', 'red'))
)
})
# Observe changes in the selected_cells input and update cell colors accordingly
observe({
selected <- input$selected_cells
if (!is.null(selected)) {
rows <- selected$row
cols <- selected$col
# Get the JavaScript code to update cell colors based on the selected cells
js_code <- ""
for (i in seq_along(rows)) {
row <- rows[i]
col <- cols[i]
cell_css <- sprintf("#table1 tbody tr:eq(%d) td:eq(%d)", row, col)
js_code <- paste0(js_code, sprintf("$('%s').toggleClass('selected-cell-color').toggleClass('default-cell-color');", cell_css))
}
session$sendCustomMessage(type = 'jsCode', list(code = js_code))
}
})
}
shinyApp(ui, server)
2
Answers
I restarted from the very beginning, without considering the colors. now it seems that I got almost the solution. I must mix it with the previous code and fix some bugs while clicking on the case (I do not figure out why there is a "gap" while clicking).
Here is what I get with my code outside Shiny:
That works as expected. But in Shiny, that doesn’t work: once a cell is erased, it immediately reappears with the original data. I don’t know how to solve this issue yet, except by putting the DT table directly in the UI, but maybe you need to use reactivity.
Edit
I understand now: put
server = FALSE
inrenderDT
and it works.The code: