How to make R datatables column width dependant on cell content width? - r

I know you can use options to adjust width by hand. Something along the lines:
DT::datatable(mtcars,
options = list(
autoWidth = TRUE,
columnDefs = list(list(width = '70px',
targets = which(colnames(mtcars) %in% c("gear"))),
list(width = '100px',
targets = which(colnames(mtcars) %in% c("disp", "hp")))
)
)
)
I see no way to adjust the column width to fit content, without hardcoded values. I found a datatables function columns.adjust() which theoretically re-adjusts column width to input, but how to use it in R / R shiny?
MRE:
library(shiny)
library(DT)
ui <- fluidPage(
DT::dataTableOutput(outputId = "table")
)
server <- function(input, output) {
output$table <- DT::renderDataTable({
DT::datatable(mtcars,
options = list(
autoWidth = TRUE, # here we set fixed width. how to set depending on input?
columnDefs = list(list(width = '70px',
targets = which(colnames(mtcars) %in% c("gear"))),
list(width = '100px',
targets = which(colnames(mtcars) %in% c("disp", "hp")))
)
)
)
})
}
shinyApp(ui, server)

Related

RShiny: Reducing bootstrap table cell padding when using `bs_theme()`

# Header
## Load packages
library(shiny)
library(tidyverse)
library(DT)
library(bslib)
ui <- navbarPage("Test",
inverse = T,
collapsible = T,
theme = bs_theme(
version = 4,
bootswatch = "lux",
"font-size-base" = "1rem",
"table-cell-padding" = ".4rem"
),
tabPanel(
title = "Data Tables",
id = "data",
icon = icon("flask"),
fluidRow(
dataTableOutput("DT1")
)
)
)
server <- function(input, output, session) {
output$DT1 <- renderDataTable({
datatable(mtcars,
style = "bootstrap4",
options = list(info = F,
searching = T,
paging = T,
autoWidth = T,
scrollX = T),
filter = list(position = 'top', clear = FALSE),
class = 'cell-border stripe compact',
rownames = F)
})
}
##
shinyApp(ui, server)
I am trying to create a table using the bs_theme() function from the bslib package, specifically using the theme lux. The tables are very large and the cells have massive padding. Adding the "table-cell-padding" argument in bs_theme() doesn't change the cell sizes. How do I make the table compact and the cells tight around the numbers/column names?
Just add tags$style('#DT1 td {padding: 0}'), how simple is that.
# Header
## Load packages
library(shiny)
library(tidyverse)
library(DT)
library(bslib)
ui <- navbarPage("Test",
inverse = T,
collapsible = T,
theme = bs_theme(
version = 4,
bootswatch = "lux",
"font-size-base" = "1rem",
"table-cell-padding" = ".4rem"
),
tabPanel(
title = "Data Tables",
id = "data",
icon = icon("flask"),
fluidRow(
tags$style('#DT1 td {padding: 0}'),
# style = "width: 50%",
dataTableOutput("DT1")
)
)
)
server <- function(input, output, session) {
output$DT1 <- renderDataTable({
datatable(mtcars,
style = "bootstrap4",
options = list(info = F,
searching = T,
paging = T,
autoWidth = T,
scrollX = T),
filter = list(position = 'top', clear = FALSE),
class = 'cell-border stripe compact',
rownames = F)
})
}
##
shinyApp(ui, server)
Change the padding: 0 to any valid css unit you want, here I changed to 0, no padding. It will seem that it doesn't work on left and right, that's because you have autoWidth, it will always use the full width of the parent node. So, if you want the width to be smaller, uncomment the style line above, you should see is't only half of the screen.

R Shiny DT data table column width works on ALL columns, but not on specific column

I have looked at the documentation, and examples, and other answers. But, for the life of me, I can't get the DT::datatable() to widen just one column in my output. When I set the option to include _all columns, it works, but obviously not what I want.
Here is a working example:
library(dplyr)
library(shiny)
library(DT)
library(data.table)
mtcars <- mtcars[1:5, ]
ui <- fluidPage(
fluidRow(
dataTableOutput(('mtcarsDT')),
)
)
server <- function(input, output, session) {
output$mtcarsDT <- DT::renderDataTable({
recFeedbackCol <- lapply(1:nrow(mtcars), function(recnum)
as.character(
radioButtons(
paste0(
'rec', recnum),
'',
choices = c('good' = 'Good', 'bad' = 'Bad', 'neutral' = 'Neutral'),
inline = TRUE
)
)
)
recFeedbackCol <- tibble(feedback = recFeedbackCol)
mtcars <- bind_cols(
mtcars,
recFeedbackCol
)
mtcars %>%
DT::datatable(
escape = FALSE, selection = 'none',
extensions = 'FixedColumns',
options = list(
paging = FALSE, pageLength = 10, ordering = FALSE, scrollX = TRUE,
fixedColumns = list(leftColumns = 2),
preDrawCallback = JS(
'function() { Shiny.unbindAll(this.api().table().node()); }'
),
drawCallback = JS(
'function() { Shiny.bindAll(this.api().table().node()); } '
),
autoWidth = TRUE,
columnDefs = list(list(width = '200px', targets = 'feedback'))
)
)
})
}
shinyApp(ui = ui, server = server)
Changing targets = '_all' works. But, that widens all columns.
> packageVersion('shiny')
[1] ‘1.4.0’
> packageVersion('DT')
[1] ‘0.17’
Anything I am missing?
Update:
Now, I am using ncol(mtcars) and with some options, and the DT does not render at all. I get the columns, and zero rows in display:
library(dplyr)
library(shiny)
library(DT)
library(data.table)
mtcars <- mtcars[1:5, ]
ui <- fluidPage(
fluidRow(
dataTableOutput(('mtcarsDT')),
)
)
server <- function(input, output, session) {
output$mtcarsDT <- DT::renderDataTable({
recFeedbackCol <- lapply(1:nrow(mtcars), function(recnum)
as.character(
radioButtons(
paste0(
'rec', recnum),
'',
choices = c('good' = 'Good', 'bad' = 'Bad', 'neutral' = 'Neutral'),
inline = TRUE
)
)
)
recFeedbackCol <- tibble(feedback = recFeedbackCol)
mtcars <- bind_cols(
mtcars,
recFeedbackCol
)
mtcars %>%
DT::datatable(
extensions = 'FixedColumns',
rownames = FALSE,
escape = FALSE,
class="compact cell-border",
options = list(
pageLength = 15,
lengthChange = FALSE,
scrollX = TRUE,
searching = FALSE,
dom = 't',
ordering = TRUE,
fixedColumns = list(leftColumns = 2),
preDrawCallback = JS(
'function() { Shiny.unbindAll(this.api().table().node()); }'
),
drawCallback = JS(
'function() { Shiny.bindAll(this.api().table().node()); } '
),
autoWidth = TRUE,
columnDefs = list(
list(width = '200px', targets = ncol(mtcars))
)
)
)
})
}
shinyApp(ui = ui, server = server)
For targets you can use a column index number (indexing is zero-based):
targets = 3
Or to target multiple specific columns, use an array:
targets = list(3,4)
There are some additional options, too - see here for reference.
Update
So, using the fact mentioned above about indexing being zero-based, and looking at the reference documentation (see the above link), we can say the following:
The first column in the table has an index of 0.
The second column in the table has an index of 1.
... and so on.
And we can also say:
The last column in the table has an index of -1.
The second-to-last column in the table has an index of -2.
... and so on.

Set column width while using scrollX in R

I'm trying to change the width of some columns in a DT::datatable, unfortunately using the columnDefs option only appears to work when there are a small number of columns in the data.
When I add all the columns to my data the column widths no longer follow what I put in the columnDefs options.
Here is an example, as you can see the 1st table the width are all constant, whereas in the second table I have been able to manual set the widths as I desire. Removing the scrollX argument doesn't work either, and given the number of columns my data has I need it in there.
library(MASS)
library(shiny)
library(DT)
ui <- fluidPage(
mainPanel(
DT::dataTableOutput("table1"),
br(),
br(),
br(),
DT::dataTableOutput("table2")
)
)
server <- function(input, output) {
output$table1 <- DT::renderDataTable({
DT::datatable(
Cars93[,-(20:27)],
rownames = FALSE,
options = list(
pageLength = 5,
autowidth = TRUE,
scrollX = TRUE,
searching = TRUE,
ordering = TRUE,
paging = TRUE,
columnDefs = list(list(width = "200px", targets = c(0:2)),
list(width = "20px", targets = 3),
list(width = "50px", targets = 4))
)
)
})
output$table2 <- DT::renderDataTable({
DT::datatable(
Cars93[,-(6:27)],
rownames = FALSE,
options = list(
pageLength = 5,
autowidth = TRUE,
scrollX = TRUE,
searching = TRUE,
ordering = TRUE,
paging = TRUE,
columnDefs = list(list(width = "200px", targets = c(0:2)),
list(width = "20px", targets = 3),
list(width = "50px", targets = 4))
)
)
})
}
shinyApp(ui, server)
What do I need to change in my code to be able to set the column widths in the 1st table like I have them in the 2nd table while will having all the columns and scrollX?
Thanks
Try:
ui <- fluidPage(
tags$head(
tags$style(
HTML("table {table-layout: fixed;}")
)
),
......
and replace autowidth with autoWidth.

Hide a column in shiny datatable but keep it searchable

The DT package in Shiny produces a table with a searchbar that searches over every column in the table. I have a column of metadata which I do not want to display in the table, but I still want the rows to come up if I search with the search bar.
For example, the app below contains a column titled searchCol . This column is just letters. I want to hide this column in the actual table, and I want to be able to search for the letter b , using the DT search bar, and have the second row show up.
Is there a way to hide the column but have it still work with the search bar?
library(shiny)
library(DT)
ui <- fluidPage(
DTOutput('tbl1'),
)
server <- function(input, output, session) {
output$tbl1 <- DT::renderDT(server = TRUE, {
datatable(
cbind(data.frame(replicate(3,sample(0:1,26,rep=TRUE))), data.frame(searchCol = letters)),
escape = FALSE,
rownames = FALSE,
filter = list(position = "top", clear = FALSE, plain = TRUE),
selection = "single",
options = list(
autoWidth = TRUE,
pageLength = 50,
lengthMenu = c(50, 100, 1000),
dom = 'Blfrtip',
buttons = c('copy', 'excel')
)
)
})
}
shinyApp(ui, server)
I've adapted the answer from here to the format you need to use in DT::datatable. You can use columnDefs to define the render options for the different columns, targets defines which column you mean. Please note that the JS library datatables starts counting columns at 0.
library(shiny)
library(DT)
ui <- fluidPage(
DTOutput('tbl1'),
)
server <- function(input, output, session) {
output$tbl1 <- DT::renderDT(server = TRUE, {
datatable(
cbind(data.frame(replicate(3,sample(0:1,26,rep=TRUE))), data.frame(searchCol = letters)),
escape = FALSE,
rownames = FALSE,
filter = list(position = "top", clear = FALSE, plain = TRUE),
selection = "single",
options = list(
autoWidth = TRUE,
pageLength = 50,
lengthMenu = c(50, 100, 1000),
dom = 'Blfrtip',
buttons = c('copy', 'excel'),
columnDefs = list(
list(
targets = 3,
searchable = TRUE,
visible = FALSE
)
)
)
)
})
}
shinyApp(ui, server)

filter = 'top' does not execute in Shiny app

I just created the following Shiny app using DT. My problem is that filter='top' does not actually seem to execute. Is there a problem combining checkboxGroupInput and filter from DT? I was hoping to be able to add as many filtering options as possible.
ui.R
library(shiny)
shinyUI(pageWithSidebar(
headerPanel('Database'),
sidebarPanel(
p('Welcome to the Database.'),
p('(1) Use the dropdown menus to select a category, type, or manufacturer.'),
p('(2) Use the checkboxes below to add or remove information from the table.'),
checkboxGroupInput('show_vars', 'Information:', names(Foods),
selected = names(Foods)),
),
mainPanel(
fluidRow(h1('A Server-side Table')),
fluidRow(
column(9, DT::dataTableOutput('x3')),
column(3, verbatimTextOutput('x4'))
)
)
)
)
server.R
library(shiny)
library(DT)
shinyServer(function(input, output, session) {
# server-side processing
output$x3 = DT::renderDataTable(Foods[, input$show_vars, drop = TRUE], server = TRUE,
options = list(pageLength = 5, autoWidth = TRUE,
columnDefs = list(list(width = '200px', targets = "_all"))
, filter = 'top'))
# print the selected indices
output$x4 = renderPrint({
s = input$x3_rows_selected
if (length(s)) {
cat('These rows were selected:\n\n')
cat(s, sep = ', ')
}
})
}
)
Move the filter='top' out to renderDataTable like this:
output$x3 = DT::renderDataTable(iris[, input$show_vars, drop = TRUE], server = TRUE,
options = list(pageLength = 5, autoWidth = TRUE,
columnDefs = list(list(width = '200px', targets = "_all"))
),filter='top')

Resources