DT icon indicating copy to clipboard operation
DT copied to clipboard

How to select the entire row, while having only first(one) column clickable?

Open avraam-inside opened this issue 1 year ago • 5 comments

I need to do the same as here:

https://editor.datatables.net/examples/inline-editing/simple.html

Ideally with a checkbox.

With the select extension, this is configured simply: select = list(style = 'multi+shift', selector = "td:first-child"),

Only via the built-in selectable that works with the server-side on the R (Shiny)...

I need to make only the cells of the first non-editable column selectable, because selectable and editable are not very conveniently combined together in one cell

P.S.: My SO question: https://stackoverflow.com/questions/76473946/how-to-select-the-entire-row-while-having-only-firstone-column-clickable


By filing an issue to this repo, I promise that

  • [x] I have fully read the issue guide at https://yihui.org/issue/.
  • [x] I have provided the necessary information about my issue.
    • If I'm asking a question, I have already asked it on Stack Overflow or RStudio Community, waited for at least 24 hours, and included a link to my question there.
    • If I have posted the same issue elsewhere, I have also mentioned it in this issue.
  • [x] I have learned the Github Markdown syntax, and formatted my issue correctly.

I understand that my issue may be closed if I don't fulfill my promises.

avraam-inside avatar Jun 16 '23 07:06 avraam-inside

In Shiny?

stla avatar Jun 17 '23 17:06 stla

Yes

avraam-inside avatar Jun 17 '23 18:06 avraam-inside

Hmm I thought I had a solution but there's a problem. Try the app below. When you click on a checkbox, the row is selected as expected. The problem is that if you click elsewhere in the table, this unselects the selected rows.

Any idea, @yihui ?

library(shiny)
library(DT)

checkboxColumn <- function(len, ...) { 
  inputs <- character(len)
  for(i in seq_len(len)) {
    inputs[i] <- as.character(
      checkboxInput(paste0("checkb_", i), label = NULL, ...)
    )
  }
  inputs
}

dat <- data.frame(
  checkme = checkboxColumn(4),
  fruit   = c("apple", "cherry", "pineapple", "pear"),
  letter  = c("a", "b", "c", "d")
)

callback <- c(
  sprintf("var selected = [%s];", toString(rep("false", 4))),
  "$('body').on('click', '[id^=checkb]', function(){",
  "  var id = this.getAttribute('id');",
  "  var i = parseInt(/checkb_(\\d+)/.exec(id)[1]);",
  "  var value = $(this).prop('checked');",
  "  selected[i-1] = value; console.log(selected);",
  "  Shiny.setInputValue('selected', selected);",
  "})"
)

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

server <- function(input, output, session) {
  
  output[["dtable"]] <- renderDT({
    datatable(
      dat, 
      escape = FALSE,
      selection = list(
        mode = "multiple", selected = NULL, target = "row", selectable = FALSE
      ),
      callback = JS(callback)
    )
  })
  
  proxy <- dataTableProxy("dtable")
  
  observeEvent(input[["selected"]], {
    selected <- which(as.logical(input[["selected"]]))
    print(selected)
    selectRows(proxy, selected, ignore.selectable = TRUE)
  })
  
}

shinyApp(ui, server)

stla avatar Jun 17 '23 18:06 stla

Ok, I found a solution. There is some flickering but it works.

library(shiny)
library(DT)

checkboxColumn <- function(len, ...) { 
  inputs <- character(len)
  for(i in seq_len(len)) {
    inputs[i] <- as.character(
      checkboxInput(paste0("checkb_", i), label = NULL, ...)
    )
  }
  inputs
}

dat <- data.frame(
  checkme = checkboxColumn(4),
  fruit   = c("apple", "cherry", "pineapple", "pear"),
  letter  = c("a", "b", "c", "d")
)

callback <- c(
  sprintf("var selected = [%s];", toString(rep("false", 4))),
  "$('body').on('click', '[id^=checkb]', function() {",
  "  var id = this.getAttribute('id');",
  "  var i = parseInt(/checkb_(\\d+)/.exec(id)[1]);",
  "  var value = $(this).prop('checked');",
  "  selected[i-1] = value;",
  "  Shiny.setInputValue('selected', selected);",
  "});",
  "table.on('click', 'tr', function() {",
  "  Shiny.setInputValue('selected', selected, {priority: 'event'});",
  "});"
)

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

server <- function(input, output, session) {
  
  output[["dtable"]] <- renderDT({
    datatable(
      dat, 
      rownames = FALSE,
      escape = FALSE,
      selection = list(
        mode = "multiple", selected = NULL, target = "row", selectable = FALSE
      ),
      callback = JS(callback)
    )
  })
  
  proxy <- dataTableProxy("dtable")
  
  observeEvent(input[["selected"]], {
    selected <- which(as.logical(input[["selected"]]))
    selectRows(proxy, selected, ignore.selectable = TRUE)
  })
  
}

shinyApp(ui, server)

stla avatar Jun 17 '23 19:06 stla

Interesting, I'll try!

The line of code is a bit "tension" me: sprintf("var selected = [%s];", toString(rep("false", 4))) The table rows will be filled in/deleted in the process, and I only change the content using DT::editData, and not rebuild the UI entirely

avraam-inside avatar Jun 17 '23 19:06 avraam-inside