Column widths in renderDataTable of a shiny app without stretching - r

I would like to get a DataTable (with all its ranking, search and page features) that does not stretch fully across the page, and results in large amounts of white space in each column...
... ideally with column widths similar to the "wrap" style from renderTable...
I know I can fix relative column widths, however, my table will dynamically update with different numbers of columns dependent of inputs selected. I would prefer additional columns to expand into the empty space on the right hand side and then trigger a horizontal scrollbar if it becomes wider than the browser window width.
Reproducible example of the tables in the images above...
library(shiny)
runApp(list(
ui = navbarPage(
title = 'Tables',
tabPanel('dataTableOutput', dataTableOutput('ex1')),
tabPanel('tableOutput', tableOutput('ex2'))
),
server = function(input, output) {
output$ex1 <- renderDataTable(iris)
output$ex2 <- renderTable(iris)
}
))

I think that you should use drawCallback in dataTables. Here I just changed your example a little to fix width of dataTable to 600px. you can play with possible java script function in callback function to do almost anything.
library(shiny)
runApp(list(
ui = navbarPage(
title = 'Tables',
tabPanel('dataTableOutput', dataTableOutput('ex1')),
tabPanel('tableOutput', tableOutput('ex2'))
),
server = function(input, output) {
output$ex1 <- renderDataTable( iris,
option = list( drawCallback = I("function( settings ) {document.getElementById('ex1').style.width = '600px';}")) )
output$ex2 <- renderTable(iris)
}
))

Assuming your data.frame is df, then put this code at the beginning of the reactive/renderTable block at the server side. It will wrap the column names to desirable length and therefore reducing the size of the table. You can always change the width to equal the desired width.
library(stringr)
colnames(df) = str_wrap(colnames(df),width = 10)

Related

Apply css styling to a single DT datatable

I have a pair of DT datatables in my shiny app. I would like to control the width of one of these tables (via fixed-width) while leaving the width of the other table dynamic.
After trialing a number of approaches for fixing columns widths, the one that seems best for my application is to use the styling table.dataTable {table-layout: fixed;}. However, this effects both tables.
How can I limit the effect of this style to just the second table?
data(starwars)
starwars = starwars[1:5,1:4]
ui = fluidPage(
DT::dataTableOutput("variable_width"),
tags$style(HTML("table.dataTable {table-layout: fixed;}")),
DT::dataTableOutput("fixed_width")
)
server = function(input, output, session){
output$variable_width = DT::renderDataTable({ starwars }, options = list(dom = "t"))
output$fixed_width = DT::renderDataTable({ starwars }, options = list(dom = "t"))
}
shinyApp(ui, server)
From searching, it looks like this should be possible via div: That you can limit styling to only the current div. However, I am unable to get this working.
You need to add what are the parents div in you CSS, like this :
tags$style(HTML("#fixed_width > .dataTables_wrapper > table.dataTable {table-layout: fixed;}")),
With #fixed_width being the outputId (which is also a div id) of you second table.

How to fine-tune the positioning of objects rendered in r shiny?

I'm trying to adjust the positioning of conditionally-rendered objects in R shiny. When running the below skeleton code and clicking the "Delete" action button, I'd like to nudge the conditionally-rendered text ("Select series to delete >>") a bit to the right, and move the little selectInput() box that also conditionally appears on the far right a bit to the left, closer to "Select series to delete >>". I've fiddled with column widths, etc., and I've exhausted all the formatting options which I know of which are limited. Any suggestions for fine-tuning the positioning of these items? My guess is this would entail some CSS which I know almost nothing about.
Skeleton code:
library(dplyr)
library(shiny)
library(shinyjs)
toggleView <- function(input, output_name){
observeEvent(input$delSeries, {show(output_name)})
observeEvent(input$addSeries, {hide(output_name)})
}
ui <- fluidPage(br(),
useShinyjs(),
fluidRow(
column(1,actionButton("addSeries", "Add",width = '70px')),
column(1,actionButton("delSeries","Delete",width = '70px')),
column(3,h5((hidden((textOutput("delFlag")))))),
column(3,hidden(uiOutput("delSeries2")))
)
)
server <- function(input, output, session) {
output$delFlag <- renderText("Select series to delete >>")
output$delSeries2 <-
renderUI(
selectInput("delSeries3",
label = NULL,
choices = c(""),
selected = "",
width = '110px')
)
toggleView(input,"delSeries2")
toggleView(input,"delFlag")
}
shinyApp(ui,server)
You can add some styles to the 2 columns like so:
library(dplyr)
library(shiny)
library(shinyjs)
toggleView <- function(input, output_name){
observeEvent(input$delSeries, {hide(output_name)})
observeEvent(input$addSeries, {show(output_name)})
}
# (0)
css <- HTML("
.row .nudge-right {
padding-right:0;
}
.row .nudge-left {
padding-left:0;
}
")
ui <- fluidPage(
tags$head(tags$style(css)), # (1)
br(),
useShinyjs(),
fluidRow(
column(1,actionButton("addSeries", "Add",width = '70px')),
column(1,actionButton("delSeries","Delete",width = '70px')),
column(3,h5(hidden(textOutput("delFlag"))),
class = c("nudge-right", "text-right")), # (2)
column(3,hidden(uiOutput("delSeries2")), class = "nudge-left") # (2)
)
)
Explanation
The white space you see is partly due to the width of the column and partly due to the so called padding (an additional white space around the element). To bridge this gap you can:
Right align the text. Here you can rely on the already pre-defined (by the underlying bootstrap framework) class text-right.
Further decrease the gap by removing the right padding from the text column and the left padding from the input column. In order to so, you define new classes (I called them .nudge-right and .nudge-left respectively) where you deliberately set the padding to your liking (here I removed it completely, you may want to provide a small offset though - e.g. 5px).
Then all which is left is to
Create some css with the class definitions (#0)
Load the css (#1)
Assign the classes to the columns (#2)

How to add scroll bar and cross button to pop up window in shiny app?

I want to have scroll bar to scroll up and down, cross button to close the pop up window and default of 10 records should display instead of 25 now.
I don't know how to write code for this.
library(shiny)
library(shinydashboard)
library(shinyjs)
library(shinyBS)
data <- iris
ui <- tagList(
useShinyjs(),
dashboardPage(
dashboardHeader(title = "Telemedicine HP"),
dashboardSidebar(),
dashboardBody(
fluidRow(
div(id='clickdiv',
valueBox(60, subtitle = tags$p("Attended", style = "font-
size: 200%;"), icon = icon("trademark"), color = "purple", width = 4,
href
= NULL)
)
)
)
)
)
server <- function(input, output, session){
onclick('clickdiv', showModal(modalDialog(
title = "Your title",
renderDataTable(data)
)))
}
shinyApp(ui, server)
By clicking on valuebox a pop up window will appear showing some tabular data.
But that window should have a scroll bar, cross button in right top corner and records should be shown 10 by default instead of 25 showing now in top left corner of the pop up window.
Can anyone help me with this ?
If your server part is like this, it is limited to 10 shown per page:
server <- function(input, output, session){
onclick('clickdiv', showModal(modalDialog(
title = "Your title",
renderDataTable(data, options = list(
pageLength = 10,
scrollY = "400px"
))
)))
}
I'm not sure I understand the need for the other parts. With 10 records, you don't need to be able to scroll up and down, but even when I set this to a lot of records (say 100), the normal page scroll bar works fine. And there is a button to dismiss the table already (although I appreciate it is not the cross in the corner you are requesting).
You can change other parts of your DataTable using the options - you can see some examples here.
Hope this helps!
EDIT: I've added an option for a vertical scroll bar. You can change the number to suit you.
If that doesn't work then you might be using a setup (for eg Mac) where scrollbars are hidden by default until you start scrolling.

Hide column names in shiny table

Is there a way to hide column names of a formattable? I thought about
changing an attribute in the formattable options. Didn't find something about it in the documentation or SO.
changing the font color to white for the header. I guess this may be an easy task for a CSS expert. I couldn't find the right sources to do it as a layman.
Maybe there is another option that I didn't think of? Thanks for your help in advance.
Example code below. The right table's header should be hidden.
library(shiny)
library(formattable)
df <- data.frame(A = LETTERS[1:10], B = 1:10)
server <- function(input, output) {
output$table1 <- renderFormattable({
formattable(df)
})
output$table2 <- renderFormattable({
formattable(df)
})
}
ui <- fluidPage(
fluidRow(
column(6,
h6("Table with header"),
formattableOutput("table1")
),
column(6,
h6("Table without header"),
formattableOutput("table2")
)
)
)
shinyApp(ui = ui, server = server)
Additional: If there is a way to set cell borders like in Excel for the
right table, solutions to this problem would also be appreciated.
Not exactly hiding, but here is my simple suggestion:
output$table2 <- renderFormattable({
names(df) <- c("_", ".")
formattable(df)
})
Any help to your problem?
Add this to your code:
tags$head(tags$style(type = "text/css", "#table2 th {display:none;}"))
Note that you will need to manually set the widths of your columns as they will collapse to the smallest width without text overflowing to a new line.
What I've done here is used some CSS to tap into table2's properties. I access the header properties by declaring th after stating the table's ID. Any additional css for the header can go after the ;.

Plot does not resize 100% width after show/hide sidebar in R shiny page

I have a plot which is set to 100% width (default) in the main panel of a two-panel page in R Shiny. The sidebar is hideable through a toggle action button.
When the sidebar is visible (default), the plot fills the width of the main panel. When the sidebar is hidden, I want the plot to expand to fill 100% of the space now available, i.e. the whole browser window. But this does not happen! It keeps the same size.
library(shiny)
library(shinyBS)
UI <- fluidPage(
bsButton("showpanel", "Show/hide sidebar", type = "toggle", value = TRUE),
sidebarLayout(
conditionalPanel(condition = "input.showpanel == true",
sidebarPanel("This is my sidebar.")
),
mainPanel(plotOutput("plot", width = "100%"))
)
)
SERVER <- function(input, output) {
output$plot <- renderPlot({
plot(1:10, main = "The width of this plot adjusts\nto window resizes but not to\nshow/hide sidepanel!")
})
}
runApp(shinyApp(UI,SERVER))
Attempted so far:
Defining the plot object from within the UI file, as above.
Defining the plot object from within the server file, as a renderUI object.
Set CSS tag in the page as per tags$head(tags$style("#myplot{height:100vh !important;}")) from this question, Scaling shiny plots to window height.
Possible work-arounds:
Make the width of the plot dynamic and depending on the state of the toggle button. Then I can make the plot e.g. 140% width when the sidebar is hidden. This does not generalise well, and loses the point of using the adaptability of fluidPage.
(fluidPage changes the layout dependent on the browser window size. For example, if you make your browser window about the size of a mobile phone, it will place the sidebar above the main panel.)
#konvas answer is really good and probably the way you wan't to do this but if you want to use the sidebarLayout (and for the sake of giving another answer) you can use jQuery to toggle the bootstrap columns like:
library(shiny)
ui <- fluidPage(
tags$head(
tags$script(
HTML("
$(document).ready(function(){
// Mark columns we want to toggle
$('body').find('div [class=col-sm-4]').addClass('sidebarPanel');
$('body').find('div [class=col-sm-8]').addClass('mainPanel');
})
Shiny.addCustomMessageHandler ('resize',function (message) {
$('.sidebarPanel').toggle();
$('.mainPanel').toggleClass('col-sm-8 col-sm-12');
$(window).trigger('resize')
});
")
)
),
actionButton("showpanel", "Show/hide sidebar"),
sidebarLayout(
sidebarPanel("This is my sidebar."),
mainPanel(plotOutput("plot", width = "100%"))
)
)
server <- function(input, output, session) {
observeEvent(input$showpanel,{
session$sendCustomMessage(type = 'resize', message = 1)
})
output$plot <- renderPlot({
plot(1:10, main = "The width of this plot adjusts\nto window resizes but not to\nshow/hide sidepanel!")
})
}
runApp(shinyApp(ui,server))
The same methodology would apply if you use columns in a fluidRow or something similar.
One way to do this would be to use a reactive layout (there are many questions on this eg Switch between layouts reactively with shiny). In your case that would be something like
library(shiny)
library(shinyBS)
UI <- shinyUI(
fluidPage(
bsButton("showpanel", "Show/hide sidebar", type = "toggle", value = TRUE),
uiOutput('ui')
)
)
SERVER <- function(input, output) {
output$ui <- renderUI({
if (input$showpanel) {
sidebarLayout(
sidebarPanel("This is my sidebar."),
mainPanel(plotOutput('plot'))
)
} else {
plotOutput('plot')
}
})
output$plot <- renderPlot({
plot(1:10, main = "The width of this plot adjusts\nto window resizes and to\nshow/hide sidepanel!")
})
}
runApp(shinyApp(UI,SERVER))

Resources