排序后的单元颜色重置

发布于 2025-02-12 13:38:59 字数 1219 浏览 0 评论 0原文

我正在尝试将一个单元格在数据表中更改其背景颜色后。 stéphane's>stéphane's解决方案在一定程度上起作用,一旦表格或过滤表,颜色就会消失。有没有办法跟踪编辑的单元格,并且由于浏览器端在浏览器端被操纵而进行了颜色?

以下是Stéphane开发的解决方案,也是一个很好的可再现示例。

library(shiny)
library(shinyjs)
library(DT)

js <- HTML(
  "function colorizeCell(i, j){
    var selector = '#dtable tr:nth-child(' + i + ') td:nth-child(' + j + ')';
    $(selector).css({'background-color': 'yellow'});
  }"
)

colorizeCell <- function(i, j){
  sprintf("colorizeCell(%d, %d)", i, j)
}

ui <- fluidPage(
  useShinyjs(),
  tags$head(
    tags$script(js)
  ),
  br(),
  DTOutput("dtable")
)

dat <- iris[1:5, ]

server <- function(input, output, session){
  
  output[["dtable"]] <- renderDT({
    datatable(dat, editable = TRUE, selection = "none")
  }, server = FALSE)
  
  observeEvent(input[["dtable_cell_edit"]], {
    info <- input[["dtable_cell_edit"]]
    i <- info[["row"]]
    j <- info[["col"]]
    runjs(colorizeCell(i, j+1))
  })
  
}

shinyApp(ui, server)

I am trying to have a cell in a Datatable change its background color once it is edited. While Stéphane's solution works to an extent, the colors disappear once the table is sorted or filtered. Is there a way to track the edited cell and have it colored as the Datatable is manipulated on the browser end?

Below is the solution as developed by Stéphane and a good reproducible example to start with.

library(shiny)
library(shinyjs)
library(DT)

js <- HTML(
  "function colorizeCell(i, j){
    var selector = '#dtable tr:nth-child(' + i + ') td:nth-child(' + j + ')';
    $(selector).css({'background-color': 'yellow'});
  }"
)

colorizeCell <- function(i, j){
  sprintf("colorizeCell(%d, %d)", i, j)
}

ui <- fluidPage(
  useShinyjs(),
  tags$head(
    tags$script(js)
  ),
  br(),
  DTOutput("dtable")
)

dat <- iris[1:5, ]

server <- function(input, output, session){
  
  output[["dtable"]] <- renderDT({
    datatable(dat, editable = TRUE, selection = "none")
  }, server = FALSE)
  
  observeEvent(input[["dtable_cell_edit"]], {
    info <- input[["dtable_cell_edit"]]
    i <- info[["row"]]
    j <- info[["col"]]
    runjs(colorizeCell(i, j+1))
  })
  
}

shinyApp(ui, server)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

顾铮苏瑾 2025-02-19 13:38:59

问题在于您正在为可见的行着色,这不必与物理行对应。

一种可能的解决方案是在 server 方面进行着色,这样的

library(shiny)
library(DT)
library(dplyr)
library(functional)

dat <- iris[1:5, ]

ui <- fluidPage(
  DTOutput("dtable")
)

server <- function(input, output, session){
  
  my_table <- reactiveVal({
    new_dat <- dat %>% 
      mutate(across(everything(), list(changed = ~ FALSE)))
    ord <- c(rbind(names(dat), paste0(names(dat), "_changed")))
    new_dat %>% 
      select(all_of(ord))
  })
  
  output[["dtable"]] <- renderDT({
    targets <- which(grepl("_changed$", names(my_table()))) - 1L
    fns <- lapply(targets, function(ind) 
      function(table) {
        formatStyle(table, ind, ind + 1L,
                    backgroundColor = styleEqual(TRUE, "yellow"))
      }) %>% 
      do.call(Compose, .)
    datatable(my_table(), editable = TRUE, selection = "none", rownames = FALSE,
              options = list(columnDefs = list(list(visible = FALSE,
                                                    targets = targets)))) %>% 
      fns()
  })
  
  observeEvent(input[["dtable_cell_edit"]], {
    info <- input[["dtable_cell_edit"]]
    row <- info$row
    col <- info$col + 1L
    dat <- my_table()
    dat[row, col + 1L] <- TRUE
    dat[row, col] <- info$value
    my_table(dat)
  })
  
}

shinyApp(ui, server)

想法是:为:

  1. 为每个原始列添加后缀_changed的辅助列(在突变调用)
  2. 创建反应性,该保存当前数据,包括用户完成的更改。
  3. renderdt函数中,您可以参考reactiveVal(而不是静态data.frame.frame),隐藏助手列并使用格式>格式>格式根据关联*_更改列中的相应值对彩色单元格进行彩色。
  4. 最后一部分是通过调整您的ObserveEvent来保持客户端和服务器保持同步,在其中我们通过相应的*_更改列来标记一个单元格,并更改值客户端的更改。

nb 请注意,当JS基于0时,您的r是1,因此您需要通过-1 -1来抵消目标索引定位在JS中为0。无论出于奇怪的原因,这都不适用于formatstyle函数。


更新

以保持过滤器持续的更新,您需要存储它们并将其存储重新应用它们:

server <- function(input, output, session){
  
  filter_state <- reactiveValues(search = NULL, search_columns = NULL)
  
  format_table <- function(dat) {
    targets <- which(grepl("_changed$", names(dat))) - 1L
    fns <- lapply(targets, function(ind) 
      function(table) {
        formatStyle(table, ind, ind + 1L,
                    backgroundColor = styleEqual(TRUE, "yellow"))
      }) %>% 
      do.call(Compose, .)
    datatable(dat, editable = TRUE, selection = "none", rownames = FALSE,
              options = list(columnDefs = list(list(visible = FALSE,
                                                    targets = targets)))) %>% 
      fns()  
  }
  
  proxy <- dataTableProxy("dtable")
  
  my_table <- reactiveVal({
    new_dat <- dat %>% 
      mutate(across(everything(), list(changed = ~ FALSE)))
    ord <- c(rbind(names(dat), paste0(names(dat), "_changed")))
    new_dat %>% 
      select(all_of(ord))
  })
  
  output[["dtable"]] <- renderDT({
    isolate({
      filter_state$search <- input[["dtable_search"]]
      filter_state$search_columns <- input[["dtable_search_columns"]]
    })
    dat <- my_table()
    format_table(dat)
  })
  
  observeEvent(input[["dtable_cell_edit"]], {
    info <- input[["dtable_cell_edit"]]
    row <- info$row
    col <- info$col + 1L
    dat <- my_table()
    dat[row, col + 1L] <- TRUE
    dat[row, col] <- info$value
    my_table(dat)
  })
  
  observeEvent(c(filter_state$search, filter_state$search_columns), {
    proxy %>% 
      updateSearch(list(global = filter_state$search, 
                        columns = filter_state$search_columns))
  })
  
}

主要的想法是,渲染后,我们存储搜索字符串 - 既用于总体搜索(dtable_search),又要用于潜在的列特定搜索(dtable_search_columns) 。然后,我们要做的就是将相同的搜索应用于datatableProxy对象。

The problem is that you are coloring the visible row, which need not to correspond with the physical row.

One possible solution is to do the coloring on the server side, like this:

library(shiny)
library(DT)
library(dplyr)
library(functional)

dat <- iris[1:5, ]

ui <- fluidPage(
  DTOutput("dtable")
)

server <- function(input, output, session){
  
  my_table <- reactiveVal({
    new_dat <- dat %>% 
      mutate(across(everything(), list(changed = ~ FALSE)))
    ord <- c(rbind(names(dat), paste0(names(dat), "_changed")))
    new_dat %>% 
      select(all_of(ord))
  })
  
  output[["dtable"]] <- renderDT({
    targets <- which(grepl("_changed
quot;, names(my_table()))) - 1L
    fns <- lapply(targets, function(ind) 
      function(table) {
        formatStyle(table, ind, ind + 1L,
                    backgroundColor = styleEqual(TRUE, "yellow"))
      }) %>% 
      do.call(Compose, .)
    datatable(my_table(), editable = TRUE, selection = "none", rownames = FALSE,
              options = list(columnDefs = list(list(visible = FALSE,
                                                    targets = targets)))) %>% 
      fns()
  })
  
  observeEvent(input[["dtable_cell_edit"]], {
    info <- input[["dtable_cell_edit"]]
    row <- info$row
    col <- info$col + 1L
    dat <- my_table()
    dat[row, col + 1L] <- TRUE
    dat[row, col] <- info$value
    my_table(dat)
  })
  
}

shinyApp(ui, server)

The idea is to:

  1. Add helper columns for each original column wiht the suffix _changed (done in the mutate call)
  2. Create a reactive which holds the current data including changes done by the user.
  3. In the renderDT function you refer to the reactiveVal (instead of the static data.frame), hide the helper columns and use formatStyle to color cells according to the corresponding value in the associated *_changed column.
  4. The last piece is to keep client and server in sync by adapting your observeEvent, where we flag a cell as changed via the corresponding *_changed column and also change the value to refelct the changes on the client side.

N.B. Be aware that you R is 1 based while JS is 0 based, hence you need to offset your target index by -1 (becasue I removed the row names whihc would otherwise be positioned at 0 in JS). This, for whatever strange reason, does not apply for the formatStyle function.

Example of a Table where cells are colored according to an edit made


Update

To keep the filters persistent, you need to store them and to re-apply them:

server <- function(input, output, session){
  
  filter_state <- reactiveValues(search = NULL, search_columns = NULL)
  
  format_table <- function(dat) {
    targets <- which(grepl("_changed
quot;, names(dat))) - 1L
    fns <- lapply(targets, function(ind) 
      function(table) {
        formatStyle(table, ind, ind + 1L,
                    backgroundColor = styleEqual(TRUE, "yellow"))
      }) %>% 
      do.call(Compose, .)
    datatable(dat, editable = TRUE, selection = "none", rownames = FALSE,
              options = list(columnDefs = list(list(visible = FALSE,
                                                    targets = targets)))) %>% 
      fns()  
  }
  
  proxy <- dataTableProxy("dtable")
  
  my_table <- reactiveVal({
    new_dat <- dat %>% 
      mutate(across(everything(), list(changed = ~ FALSE)))
    ord <- c(rbind(names(dat), paste0(names(dat), "_changed")))
    new_dat %>% 
      select(all_of(ord))
  })
  
  output[["dtable"]] <- renderDT({
    isolate({
      filter_state$search <- input[["dtable_search"]]
      filter_state$search_columns <- input[["dtable_search_columns"]]
    })
    dat <- my_table()
    format_table(dat)
  })
  
  observeEvent(input[["dtable_cell_edit"]], {
    info <- input[["dtable_cell_edit"]]
    row <- info$row
    col <- info$col + 1L
    dat <- my_table()
    dat[row, col + 1L] <- TRUE
    dat[row, col] <- info$value
    my_table(dat)
  })
  
  observeEvent(c(filter_state$search, filter_state$search_columns), {
    proxy %>% 
      updateSearch(list(global = filter_state$search, 
                        columns = filter_state$search_columns))
  })
  
}

The main idea is that upon rendering, we store the search strings - both for the overall search (dtable_search) and for potential column specific searches (dtable_search_columns). Then all we have to do is to apply the same searches to the dataTableProxy object.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文