3

I have a shiny app which uses reactable to display the status of a task. The information is color-coded for a status column, the data is updated using updateReactable().

Initially the color-background is correct (eg red for open, green for done), but when I call updateReactable, the background color does not change.

An easy solution would be to always re-render the table, but I want to preserve user input (eg sort, filter, page-selection etc), therefore I use updateReactable.

An MWE looks like this:

library(shiny)
library(reactable)

ui <- fluidPage(
  reactableOutput("table"),
  actionButton("go", "Go")
)

d <- data.frame(x = sample(letters, 10, replace = TRUE), status = "Open")
d[1, "status"] <- "Done"

cols_bg <- c("Open" = "red", "Done" = "green")
cols_font <- c("Open" = "black", "Done" = "white")

server <- function(input, output, session) {
  # render this only once, update values later
  output$table <- renderReactable({
    reactable(d,
              columns = list(
                status = colDef(name = "Status", 
                                style = function(value) {
                                  list(background = cols_bg[[value]],
                                       fontWeight = 600, color = cols_font[[value]])
                                })
              )
    )
  })
  
  # on button-click replace some values with Done and update table
  observeEvent(input$go, {
    ids <- sample(nrow(d), 0.7*nrow(d))
    d$status[ids] <- "Done"
    updateReactable("table", d)
  })
}

shinyApp(ui, server)

After the click it looks something like this:

enter image description here

Note that all Done fields should be green.

David
  • 9,216
  • 4
  • 45
  • 78
  • 1
    I'm not familiar with reactable, but i ran your code with a `cat(value, "\n")` as the first line of the style function (approx. line 22), and it shows that the styling function only runs when the app starts, and not when ´updateReactable("table", d)` runs. This is just to give you an idea of what is wrong. A quick solution to this could be have the whole reactable within a `uiOutput()` and then have an observer that updates this ui element. But i'm not sure if this goes against your requirements – brendbech Jun 27 '22 at 08:37

2 Answers2

2

As per this article for dynamic conditional styling we'll need to provide a JS function:

library(shiny)
library(reactable)

ui <- fluidPage(reactableOutput("table"),
                actionButton("go", "Go"))

d <- data.frame(x = sample(letters, 10, replace = TRUE), status = "Open")
d[1, "status"] <- "Done"

server <- function(input, output, session) {
  output$table <- renderReactable({
    reactable(d,
              columns = list(status = colDef(
                style =  JS(
                  "function(rowInfo) {
                                if (rowInfo.values['status'] == 'Open') {
                                  return { backgroundColor: 'red', color: 'black', fontWeight: 600}
                                } else if (rowInfo.values['status'] == 'Done') {
                                  return { backgroundColor: 'green', color: 'white', fontWeight: 600 }
                                } else {
                                  return { backgroundColor: 'grey', color: 'black', fontWeight: 600 }
                                }
                              }"
                )
              )))
  })
  
  # on button-click replace some values with Done and update table
  observeEvent(input$go, {
    ids <- sample(nrow(d), 0.7 * nrow(d))
    d$status[ids] <- "Done"
    updateReactable("table", d)
  })
}

shinyApp(ui, server)

result

ismirsehregal
  • 30,045
  • 5
  • 31
  • 78
0

I couldn't get this to work, but then I am not an expert on reactable. A possible work-around is to use reactiveValues and manually set the selection/filtering/etc. while updating the table.

The example below shows a solution for selected.

library(reactable)
library(shiny)

ui <- fluidPage(
  reactableOutput("table"),
  actionButton("go", "Go")
)

d <- data.frame(x = sample(letters, 10, replace = TRUE), status = "Open", stringsAsFactors = FALSE)
d[1, "status"] <- "Done"

cols_bg <- c("Open" = "red", "Done" = "green")
cols_font <- c("Open" = "black", "Done" = "white")

server <- function(input, output, session) {
  values <- reactiveValues(d = d)
  # render this only once, update values later
  selected <- reactive({
    getReactableState("table", "selected")
    })
  
  output$table <- renderReactable({
    reactable(values$d, selection = "multiple", onClick = "select", defaultSelected = selected(),
              columns = list(
                status = colDef(name = "Status", 
                                style = function(value) {
                                  list(background = cols_bg[[value]],
                                       fontWeight = 600, color = cols_font[[value]])
                                })
              )
    )
  })
  
  # on button-click replace some values with Done and update table
  observeEvent(input$go, {
    ids <- sample(nrow(d), 0.7*nrow(d))
    values$d$status[ids] <- "Done"
    # updateReactable("table", values$d)
  })
}

shinyApp(ui, server)
Tom
  • 532
  • 3
  • 11