DT
DT copied to clipboard
Can't make first column selectable when rownames is FALSE
It seems there is some inconsistent behavior of the indexing in selectable when rownames is FALSE.
Suppose I want to make a few cells in the first column selectable by referencing column 0. The following throws an error that "selectable must be either all positive or all non-positive values"
library(shiny)
library(DT)
shinyApp(
ui = fluidPage(fluidRow(column(12, DTOutput('tbl')))),
server = function(input, output) {
output$tbl = renderDT(
iris,
rownames = FALSE,
selection = list(mode = 'single', target = 'cell',
selectable = cbind(1:5,0))
)
}
)

Yet if I reference column 1, the second data column is selectable implying zero-based indexing is respected.
library(shiny)
library(DT)
shinyApp(
ui = fluidPage(fluidRow(column(12, DTOutput('tbl')))),
server = function(input, output) {
output$tbl = renderDT(
iris,
rownames = FALSE,
selection = list(mode = 'single', target = 'cell',
selectable = cbind(1:5,1))
)
}
)

The behavior is slightly different but still inconsistent when selecting by column.
When rownames are included, making column 0 selectable is interpreted as the first data column (not counting the rowname column).
library(shiny)
library(DT)
shinyApp(
ui = fluidPage(fluidRow(column(12, DTOutput('tbl')))),
server = function(input, output) {
output$tbl = renderDT(
iris,
rownames = TRUE,
selection = list(mode = 'single', target = 'column', selectable = 0)
)
}
)

But if rownames are off, requesting column 0 to be selectable results in all other columns except the first data column being selectable.
library(shiny)
library(DT)
shinyApp(
ui = fluidPage(fluidRow(column(12, DTOutput('tbl')))),
server = function(input, output) {
output$tbl = renderDT(
iris,
rownames = FALSE,
selection = list(mode = 'single', target = 'column', selectable = 0)
)
}
)

> xfun::session_info('DT')
R version 4.0.4 (2021-02-15)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Big Sur 11.6.1, RStudio 2021.9.0.351
Locale: en_US.UTF-8 / en_US.UTF-8 / en_US.UTF-8 / C / en_US.UTF-8 / en_US.UTF-8
Package version:
base64enc_0.1.3 crosstalk_1.2.0 digest_0.6.28 DT_0.20.1
fastmap_1.1.0 graphics_4.0.4 grDevices_4.0.4 htmltools_0.5.2
htmlwidgets_1.5.4 jquerylib_0.1.4 jsonlite_1.7.2 later_1.3.0
lazyeval_0.2.2 magrittr_2.0.1 methods_4.0.4 promises_1.2.0.1
R6_2.5.1 Rcpp_1.0.7 rlang_0.4.12 stats_4.0.4
utils_4.0.4 yaml_2.2.1
By filing an issue to this repo, I promise that
- [x] I have fully read the issue guide at https://yihui.name/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'm filing a bug report, I have included a minimal, self-contained, and reproducible example, and have also included
xfun::session_info('DT')
. I have upgraded all my packages to their latest versions (e.g., R, RStudio, and R packages), and also tried the development version:remotes::install_github('rstudio/DT')
. - 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.
This is actually expected... However, I do admit that it's very confusing...
The thing is that the zero index is treated as a "negative number" which means "non-selectable".
So,
- 0 can't be used with other positive numbers, like
cbind(1, 0)
- "rowname = true & column with selectable = 0" means the rowname column is non-selectable, so you can select column 1 and plus
- "rowname = false & column with selectable = 0" means the first column is non-selectable, so you can only select column 2 and plus
I do agree that with rownames=FALSE
and selection.mode='cell'
, making the cells in the first column being selectable is difficult, as you have to construct a big non-selectable matrix.
As of your example, the solution would be below:
library(shiny)
library(DT)
mat = expand.grid(seq_len(nrow(iris)), seq_len(ncol(iris)) - 1)
mat = as.matrix(mat)
mat = mat[-(1:5),]
shinyApp(
ui = fluidPage(fluidRow(column(12, DTOutput('tbl')))),
server = function(input, output) {
output$tbl = renderDT(
iris,
rownames = FALSE,
selection = list(mode = 'single', target = 'cell',
selectable = -mat)
)
}
)
Finally, if you have any ideas on how to improve this behavior, we're happy to hear.
Thank you for the detailed response! I agree that it's clunky to have to input a matrix of all other indices in order to apply this option on the first column. Although I don't have any obvious suggestions to get around that.
It'd be nice to have a consistent API e.g. with one-based column indexing in R and then do the accounting to provide zero-based indices to JS behind the scenes. I know that would mean selection
runs counter to the zero-based JS column indices needed in options
, which doesn't seem great. But a bonus would mean rows and columns both use consistent one-based indices in selection
on the R side, which could make the usage less confusing (especially for matrix examples).
I would just push back a bit on the assumption in the original PR that row names are usually shown and thus desired to be not selectable.
Thanks again and I appreciate all of your work on this package!
What currently worked for me is keeping rownames = TRUE
, but setting all names to an empty string and making the column as small as possible.
Note that rownames(data) <- rep("", nrow(data))
does not work since rownames are expected to be unique.
MWE:
library(shiny)
library(DT)
shinyApp(
ui = fluidPage(fluidRow(column(12, DTOutput('tbl')))),
server = function(input, output) {
output$tbl = renderDT({
data <- iris
attr(data, "row.names") <- rep("", nrow(data))
datatable(
data,
rownames = TRUE,
options = list(
columnDefs = list(
list(
width = '0px',
targets = c(0)) # make row names as small as possible
)
),
selection = list(mode = 'single', target = 'cell',
selectable = cbind(1:5,1)
)
)
}
)
}
)
Note that there still is a visible, yet barely noticeable, column on the left. It's not really resolving the core issue, but at least easy to implement/understand as a workaround.
Can't we simply hide the row names column (visible=FALSE
)?
@stla
That's indeed the better approach, thank you!
FWIW, I now realized I was working on a package where this trick was used:
https://stackoverflow.com/questions/60406027/how-to-disable-double-click-reactivity-for-specific-columns-in-r-datatable
CSS <- "
table tbody td:nth-child(1) {pointer-events: none;}
table tbody td:nth-child(1)>div {pointer-events: auto;}
"
So please ignore my comment, I needlessly over-complicated things.