I want a checkbox that selects all the rows displayed (displayed is key as this differs between the filters you have applied and the entire data table) in a standard DT::renderDataTable in Shiny.
Is there any DT extension that already does this? My coding skills are basic so I cannot write an equivalent Java or HTML code.
This is my app so far, any csv file is compatible for the select all purpose. At the moment there is a clunky way of creating another table of all the selected rows (manually selected one by one) - this is difficult when you want to select 30 animals all with the same characteristic.
library(shiny)
library(shinyjs)
library(DT)
library(dplyr)
library(data.table)
ui = pageWithSidebar(
headerPanel(""),
#This is where the full animal information file is input, as a ".txt" file.
sidebarPanel(
fileInput("ani", "Upload Animal Information File", accept = ".csv"),
br(),
numericInput("groups","Number of Ewe Groups", value = 1 ),
#This is a list of the table headers. These headers can be indivdually selected to be part of the concatenated "Unique ID" single column.
uiOutput("choose_columns"),
width = 2),
mainPanel(
DT::dataTableOutput("ani1"),
DT::dataTableOutput("selectedEwes")
))
server = function(input, output, session) {
animalinformation <- reactive({
file1 <- input$ani
if (is.null(file1))
return(NULL)
#This removes the Ewes and Status non-zero Rams from the displayed data, so that only live/at hand Rams are shown for selection.
isolate({
anifile <- read.csv(file1$datapath, header = TRUE)
anifile <- as.data.frame(anifile)
})
anifile
})
output$choose_columns <- renderUI({
if (is.null(animalinformation()))
return()
colnames <- names(animalinformation())
# Create the checkboxes and select them all by default
checkboxGroupInput("columns", "Choose Columns",
choices = colnames,
selected = colnames)
})
#This line is repsonsible for creating the table for display.
output$ani1 = DT::renderDataTable({
if (is.null(animalinformation()))
return()
if (is.null(input$columns) || !(input$columns %in% names(animalinformation()))) { return() }
{ datatable(animalinformation()[, input$columns, drop = F], filter = "top") }
})
ani1_selected <- reactive({
ids <- input$ani1_rows_selected
animalinformation()[ids,]
})
#This displays the table of selected rows from the table of Rams. This table can be downloaded or printed, or copied using the buttons that appear above the table, thanks to the 'Buttons' extension.
output$selectedEwes <- DT::renderDataTable({
datatable(
ani1_selected(),
selection = list(mode = "none"),
caption = "Copy to clipboard, download a .csv or print the following table of selected Ewes, using the above buttons.", extensions = 'Buttons', options = list(dom = 'Bfrtip', buttons = c('copy', 'csv', 'excel', 'pdf', 'print'))
)
})
}
shinyApp(ui = ui, server = server)
Any help would be much appreciated thanks.
Here is the simplest implementation I can think of. It takes advantage of the fact that DT will return the filter row indexes back to R, which is input$dt_rows_all in the below example. Moreover, it uses the DT::dataTableProxy() to control the row selection. Finally, it works in both the client mode and the server-side processing mode.
By the way, I want to mention that using javascript to mimic the selecting / deselecting events in DT won't change the related shiny binding values in R (e.g., input$dt_rows_selected). It's because DT has its own implementation of row selections (may change in the future but not yet at the time of writing). See rstudio/DT#366 if you want to know more.
library(shiny)
ui <- tagList(
DT::DTOutput("dt"),
checkboxInput("dt_sel", "sel/desel all"),
h4("selected_rows:"),
verbatimTextOutput("selected_rows", TRUE)
)
server <- function(input, output, session) {
dat <- reactive({iris})
output$dt <- DT::renderDT(dat(), server = TRUE)
dt_proxy <- DT::dataTableProxy("dt")
observeEvent(input$dt_sel, {
if (isTRUE(input$dt_sel)) {
DT::selectRows(dt_proxy, input$dt_rows_all)
} else {
DT::selectRows(dt_proxy, NULL)
}
})
output$selected_rows <- renderPrint(print(input$dt_rows_selected))
}
shiny::runApp(list(ui = ui, server = server))
Related
I'm working on a shiny app and trying to use renderDataTable and renderUI on the same page. The following codes work fine. But when I switch the order of 2. render select input and 3. render data table, it seems input$selected_variable cannot reflect what is selected on the shiny app. Neither the print(input$selected_variable) can print the selected value in the terminal, nor the textOutput can show the selected value on the app interface. Does anyone know why this may happen? Another follow-up question is that if I remove filter = 'top' in the renderDataTable, this issue won't occur either no matter which order is applied. Does this problem has anything to do with the filter = 'top'? (This filter = 'top' option in dataTable seemed also cause a few other weird behaviors when I tried out some other shiny app designs.)
if (interactive()) {
library(shiny)
library(DT)
shinyApp(
### ui
ui = fluidPage(
fileInput('input_data_file',
label = "Load input data file(s)",
multiple = TRUE,
buttonLabel = "Browse or Drop...",
placeholder = "(multiple) csv file(s)"),
dataTableOutput('input_table'),
uiOutput('params'),
verbatimTextOutput("text", placeholder = T),
plotlyOutput('plot')
),
### server
server = function(input, output) {
### values placeholder
values <- reactiveValues()
values$data <- NULL
### 1. upload data file
observeEvent(input$input_data_file, {
req(input$input_data_file)
file <- input$input_data_file
ext <- tools::file_ext(file$datapath)
req(file)
validate(need(ext == "csv", "Please upload a csv file"))
values$data <- read.csv(file$datapath, stringsAsFactors = FALSE)
})
### 2. render select input
output$params <- renderUI({
fluidRow(column(6, selectInput("selected_variable", "Variable:",
choices = setdiff(names(values$data), 'CustomerID')
)))
})
### 3. render data table
output$input_table <- DT::renderDataTable(
values$data,
filter = 'top', # removing this line can also solve the issue
options = list(scrollX=TRUE, scrollCollapse=TRUE, stateSave = TRUE, search = list(regex = TRUE)),
server = FALSE)
### 4. render text output
observe({
#req(input$selected_variable)
print(input$selected_variable)
output$text <-renderText({input$selected_variable})
})
}
)
}
I'm working on a shiny app and I'm running into difficulty with observeEvent() function when creating a complex expression of multiple inputs that all derive from selectInput().
My issue is some of the expressions within the observeEvent() function are triggered at startup, causing the event to prematurely execute (i.e. my actionButton() is disabled at startup, as it should be, but becomes enabled when at least one of the inputs are selected when ideally I would want it to become enabled only when ALL inputs are selected). As seen below:
observeEvent({
#input$cohort_file
input$cohort_IDvar
input$cohort_index_date
input$cohort_EOF_date
input$cohort_EOF_type
input$cohort_Y_name
input$cohort_L0
}, {
enable("set_cohort_button")
})
For reference, I'm using the shinyjs package by #daattali found on github to enable/disable actionButton().
All but the last input (i.e. input$cohort_L0) appear to be initialized at startup so observeEvent() enables actionButton only when input$cohort_L0 is selected. If you run my app and select input in sequential order from top to bottom, it appears that observeEvent() is working as intended. I only discovered that it wasn't working as intended when I decided to choose inputs at random and discovered that selecting input$cohort_L0 was the only input I needed to select to enable actionButton().
The UI portion of the code looks like this:
# Variable selection
selectInput('cohort_IDvar', 'ID', choices = ''),
selectInput('cohort_index_date', 'Index date', choices = ''),
selectInput('cohort_EOF_date', 'End of follow-up date', choices = ''),
selectInput('cohort_EOF_type', 'End of follow-up reason', choices = ''),
selectInput('cohort_Y_name', 'Outcome', choices = ''),
selectInput('cohort_L0', 'Baseline covariate measurements', choices = '', multiple=TRUE, selectize=TRUE),
And I'm using observe() to collect the column names of an upload data-set to direct them to selectInput() as follows:
### Collecting column names of dataset and making them selectable input
observe({
value <- c("",names(cohort_data()))
updateSelectInput(session,"cohort_IDvar",choices = value)
updateSelectInput(session,"cohort_index_date",choices = value)
updateSelectInput(session,"cohort_EOF_date",choices = value)
updateSelectInput(session,"cohort_EOF_type",choices = value)
updateSelectInput(session,"cohort_L0",choices = value)
})
I've looked into using the argument ignoreInit = TRUE but it does nothing for my case of having multiple expressions within observeEvent(). I've also looked into forcing no default selection in selectInput() but had no luck with that.
So my two-part question is how can I execute observEvent() when only ALL inputs are selected/how do I stop from the inputs from being initialized at startup?
My entire code:
library(shiny)
library(shinyjs)
ui <- fluidPage(
useShinyjs(),
navbarPage("Test",
tabPanel("Cohort",
sidebarLayout(
sidebarPanel(
fileInput("cohort_file", "Choose CSV File",
multiple = FALSE,
accept = c("text/csv",
"text/comma-separated-values,text/plain",
".csv")),
# Horizontal line ----
tags$hr(),
# Variable selection
selectInput('cohort_IDvar', 'ID', choices = ''),
selectInput('cohort_index_date', 'Index date', choices = ''),
selectInput('cohort_EOF_date', 'End of follow-up date', choices = ''),
selectInput('cohort_EOF_type', 'End of follow-up reason', choices = ''),
selectInput('cohort_Y_name', 'Outcome', choices = ''),
selectInput('cohort_L0', 'Baseline covariate measurements', choices = '', multiple=TRUE, selectize=TRUE),
# Horizontal line ----
tags$hr(),
disabled(
actionButton("set_cohort_button","Set cohort")
)
#actionButton("refresh_cohort_button","Refresh")
),
mainPanel(
DT::dataTableOutput("cohort_table"),
tags$div(id = 'cohort_r_template')
)
)
)
)
)
server <- function(input, output, session) {
################################################
################# Cohort code
################################################
cohort_data <- reactive({
inFile_cohort <- input$cohort_file
if (is.null(inFile_cohort))
return(NULL)
df <- read.csv(inFile_cohort$datapath,
sep = ',')
return(df)
})
rv <- reactiveValues(cohort.data = NULL)
rv <- reactiveValues(cohort.id = NULL)
rv <- reactiveValues(cohort.index.date = NULL)
rv <- reactiveValues(cohort.eof.date = NULL)
rv <- reactiveValues(cohort.eof.type = NULL)
### Creating a reactiveValue of the loaded dataset
observeEvent(input$cohort_file, rv$cohort.data <- cohort_data())
### Displaying loaded dataset in UI
output$cohort_table <- DT::renderDataTable({
df <- cohort_data()
DT::datatable(df,options=list(scrollX=TRUE, scrollCollapse=TRUE))
})
### Collecting column names of dataset and making them selectable input
observe({
value <- c("",names(cohort_data()))
updateSelectInput(session,"cohort_IDvar",choices = value)
updateSelectInput(session,"cohort_index_date",choices = value)
updateSelectInput(session,"cohort_EOF_date",choices = value)
updateSelectInput(session,"cohort_EOF_type",choices = value)
updateSelectInput(session,"cohort_L0",choices = value)
})
### Creating selectable input for Outcome based on End of Follow-Up unique values
observeEvent(input$cohort_EOF_type,{
updateSelectInput(session,"cohort_Y_name",choices = unique(cohort_data()[,input$cohort_EOF_type]))
})
### Series of observeEvents for creating vector reactiveValues of selected column
observeEvent(input$cohort_IDvar, {
rv$cohort.id <- cohort_data()[,input$cohort_IDvar]
})
observeEvent(input$cohort_index_date, {
rv$cohort.index.date <- cohort_data()[,input$cohort_index_date]
})
observeEvent(input$cohort_EOF_date, {
rv$cohort.eof.date <- cohort_data()[,input$cohort_EOF_date]
})
observeEvent(input$cohort_EOF_type, {
rv$cohort.eof.type <- cohort_data()[,input$cohort_EOF_type]
})
### ATTENTION: Following eventReactive not needed for example so commenting out
### Setting id and eof.type as characters and index.date and eof.date as Dates
#cohort_data_final <- eventReactive(input$set_cohort_button,{
# rv$cohort.data[,input$cohort_IDvar] <- as.character(rv$cohort.id)
# rv$cohort.data[,input$cohort_index_date] <- as.Date(rv$cohort.index.date)
# rv$cohort.data[,input$cohort_EOF_date] <- as.Date(rv$cohort.eof.date)
# rv$cohort.data[,input$cohort_EOF_type] <- as.character(rv$cohort.eof.type)
# return(rv$cohort.data)
#})
### Applying desired R function
#set_cohort <- eventReactive(input$set_cohort_button,{
#function::setCohort(data.table::as.data.table(cohort_data_final()), input$cohort_IDvar, input$cohort_index_date, input$cohort_EOF_date, input$cohort_EOF_type, input$cohort_Y_name, input$cohort_L0)
#})
### R code template of function
cohort_code <- eventReactive(input$set_cohort_button,{
paste0("cohort <- setCohort(data = as.data.table(",input$cohort_file$name,"), IDvar = ",input$cohort_IDvar,", index_date = ",input$cohort_index_date,", EOF_date = ",input$cohort_EOF_date,", EOF_type = ",input$cohort_EOF_type,", Y_name = ",input$cohort_Y_name,", L0 = c(",paste0(input$cohort_L0,collapse=","),"))")
})
### R code template output fo UI
output$cohort_code <- renderText({
paste0("cohort <- setCohort(data = as.data.table(",input$cohort_file$name,"), IDvar = ",input$cohort_IDvar,", index_date = ",input$cohort_index_date,", EOF_date = ",input$cohort_EOF_date,", EOF_type = ",input$cohort_EOF_type,", Y_name = ",input$cohort_Y_name,", L0 = c(",paste0(input$cohort_L0,collapse=","),"))")
})
### Disables cohort button when "Set cohort" button is clicked
observeEvent(input$set_cohort_button, {
disable("set_cohort_button")
})
### Disables cohort button if different dataset is loaded
observeEvent(input$cohort_file, {
disable("set_cohort_button")
})
### This is where I run into trouble
observeEvent({
#input$cohort_file
input$cohort_IDvar
input$cohort_index_date
input$cohort_EOF_date
input$cohort_EOF_type
input$cohort_Y_name
input$cohort_L0
}, {
enable("set_cohort_button")
})
### Inserts heading and R template code in UI when "Set cohort" button is clicked
observeEvent(input$set_cohort_button, {
insertUI(
selector = '#cohort_r_template',
ui = tags$div(id = "cohort_insertUI",
h3("R Template Code"),
verbatimTextOutput("cohort_code"))
)
})
### Removes heading and R template code in UI when new file is uploaded or when input is changed
observeEvent({
input$cohort_file
input$cohort_IDvar
input$cohort_index_date
input$cohort_EOF_date
input$cohort_EOF_type
input$cohort_Y_name
input$cohort_L0
}, {
removeUI(
selector = '#cohort_insertUI'
)
})
}
# Run the application
shinyApp(ui = ui, server = server)
The code chunk that you're passing to the observeEvent as the trigger event is
{
input$cohort_IDvar
input$cohort_index_date
input$cohort_EOF_date
input$cohort_EOF_type
input$cohort_Y_name
input$cohort_L0
}
This means that, just like any other reactive code block, when ANY of these values changes, that reactive block is considered invalidated and therefore the observer will trigger. So the behaviour you're seeing makes sense.
It sounds like what you want is to execute only when all values are set. That sounds like a great use of the req() function! Try something like this:
observe({
req(input$cohort_IDvar, input$cohort_index_date, input$cohort_EOF_date, ...)
enable("set_cohort_button")
})
Note that for shinyjs::enable() specifically, you can instead use the shinyjs::toggleState() function. I think in this case the req() function is the better option though.
My Shiny App currently:
Allows upload of a .csv
Displays a DataTable
You can choose which columns are shown using a checkboxGroupInput that is a list of the table headers
When rows are selected, these rows are displayed in another data.table below. This table can be downloaded.
I want to create a column in the first table (which is then replicated in the next table), that is a concatenation of multiple columns in the table. I want these columns for concatenation to be user specific (i.e the user uses the checkboxGroupInput to select which columns for concatenation in which order).
At the moment there is a non-dynamic version of what I am after which is the "UniqueID" column in the tables.
Is this possible?
Here is a minimal reproducible example:
library(shiny)
library(shinyjs)
library(DT)
library(dplyr)
library(data.table)
ui = pageWithSidebar(
headerPanel(""),
#This is where the full animal information file is input, as a ".txt" file.
sidebarPanel(fileInput("ani","Upload Animal Information File", accept=".csv"),
br(),
#This is a list of the table headers. These headers can be indivdually selected to be part of the concatenated "Unique ID" single column.
uiOutput("choose_columns"),
width=2),
mainPanel(
DT::dataTableOutput("ani1"),
DT::dataTableOutput("selectedRams")
))
server = function(input, output, session) {
datasetInput <- reactive({
infile <- input$ani
if(is.null(infile))
return(NULL)
#This removes the Ewes and Status non-zero Rams from the displayed data, so that only live/at hand Rams are shown for selection.
isolate({
anifile <- read.csv(infile$datapath, header = TRUE)
anifile <- anifile[!(anifile$Sex == "E" | anifile$Status != 0),]
anifile <- as.data.frame(anifile)
anifile$UniqueID <- paste(anifile$flk, anifile$byr, anifile$tag, sep=".")
anifile1 <- anifile %>% select(UniqueID, everything())
})
anifile1
})
output$choose_columns <- renderUI({
if(is.null(datasetInput()))
return()
colnames <- names(datasetInput())
# Create the checkboxes and select them all by default
checkboxGroupInput("columns", "Choose Columns",
choices = colnames,
selected = colnames)
})
#This line is repsonsible for creating the table for display.
output$ani1 = DT::renderDataTable({
if(is.null(datasetInput()))
return()
if(is.null(input$columns) || !(input$columns %in% names(datasetInput())))
return()
{datatable(datasetInput()[, input$columns, drop = F] ,filter = "top")}
})
ani1_selected <- reactive({
ids <- input$ani1_rows_selected
datasetInput()[ids,]
})
#This displays the table of selected rows from the tbale of Rams. This table can be downloaded or printed, or copied using the buttons that appear above the table, thanks to the 'Buttons' extension.
output$selectedRams <- DT::renderDataTable({
datatable(
ani1_selected(),
selection = list(mode = "none"),
caption = "Copy to clipboard, download a .csv or print the following table of selected Rams, using the above buttons." ,extensions = 'Buttons', options = list(dom = 'Bfrtip', buttons = c('copy', 'csv', 'excel', 'pdf', 'print'))
)
})
}
shinyApp(ui=ui, server=server)
The data can be any .csv or .txt file - the idea is for it to be dynamic.
Also because I want the user to be able to select the order in which they want to concatenate the columns, maybe checkboxGroupInput is the wrong approach?
Below is sample data. Essentially what I am asking is how would I make a UI/server that would allow:
USER A uploads this file and wants to create a "UniqueID" column that is the concatenation of say "flk" and "byr" i.e "3449.2009" for the first 3.
USER B uploads this file and wants to create a "UniqueID" column that is the concatenation of say "ctag" and then "cflk" i.e "L1/09.3449" for the first row.
flk,tag,byr,cflk,ctag,EID,Sex,Status,tag_number,NZMW,NZTW,Group
3449,L1/09,2009,3449,L1/09,,E,4,1,,,
3449,L100/09,2009,3449,L100/09,,E,4,100,,,
3449,L106/09,2009,3449,L106/09,,E,4,106,,,
3449,07126/17,2017,3449,07126/17,0826030465207126,R,0,7126,,,Rams
Here is the simple example.
Suppose I am looking at the table, and I want to download the table excluding the Sepal.Length column.
What should I do? I try to toggle the Sepal.Length column out, and click on excel, but it stills gives me the whole data. I don't want it. Is there any way that I can manipulate this?
Maybe I am not familiar with how it operates in the backend, after I use row reorder extension, or search/filter tables, then the downloaded files is what table displays. So I want something similar when I hide the columns.
This is useful when I have too many columns in a table and sometimes I do not need all of them.
library(shiny)
shinyApp(
ui = fluidPage(DT::dataTableOutput('tbl')),
server = function(input, output) {
output$tbl = DT::renderDataTable(
iris, extensions = "Buttons",
options = list(dom = "Blfrtip",
buttons = list('copy', 'excel', 'print', 'colvis'))
)
}
)
I really appreciate your help.
You can use input$tbl_columns_selected and input$tbl_rows_selected
Here you can select rows and columns (NB : to select column click on the label below the DT)
If you want a selection only on columns then remove input$tbl_rows_selected and set target = 'column' only
library(shiny)
library(xlsx)
library(DT)
shinyApp(
ui = fluidPage(
DT::dataTableOutput('tbl')
,downloadButton("downloadData",label ="Download")
),
server = function(input, output) {
output$tbl = DT::renderDataTable(iris, selection = list(target = 'row+column'))
myFilteredData <- reactive({
df <- iris[input$tbl_rows_selected, input$tbl_columns_selected]
if (length(input$tbl_columns_selected) == 1) {
df <- as.data.frame(df)
colnames(df) <- colnames(iris)[input$tbl_columns_selected]
}
df
})
output$downloadData <- downloadHandler(
filename = function() {paste("test.xlsx")},
content = function(file) {
write.xlsx(myFilteredData(),file, row.names=FALSE)
})
}
)
I reproduced an example shiny app written by Yihui Xie (https://yihui.shinyapps.io/DT-rows/). The app uses DT::renderDataTable() which allows a row selection.
Everything works perfectly fine. I was however wondering if it's possible to reset the row selection (i.e. undo the click selection) ? I already tried it with an action button to reset s = input$x3_rows_selected (see script below).
With my current script,s = input$x3_rows_selected does indeed get emptied, I can however not refill it. Also the selected rows are still clicked (shaded)
Does anyone has an idea? Is there an option within DT::renderDataTable() to reset the selection? Or does anyone has an idea for a workaround?
Thank you!
Example form https://yihui.shinyapps.io/DT-rows/) with my modification (action button):
server.R
library(shiny)
library(DT)
shinyServer(function(input, output, session) {
# you must include row names for server-side tables
# to be able to get the row
# indices of the selected rows
mtcars2 = mtcars[, 1:8]
output$x3 = DT::renderDataTable(mtcars2, rownames = TRUE, server = TRUE)
# print the selected indices
selection <- reactive({
if (input$resetSelection)
vector() else input$x3_rows_selected
})
output$x4 = renderPrint({
if (length(selection())) {
cat("These rows were selected:\n\n")
output <- selection()
cat(output, sep = "\n")
}
})
})
ui.R
library(shiny)
shinyUI(
fluidPage(
title = 'Select Table Rows',
h1('A Server-side Table'),
fluidRow(
column(9, DT::dataTableOutput('x3')),
column(3, verbatimTextOutput('x4'),
actionButton('resetSelection',
label = "Click to reset row selection"
) # end of action button
) #end of column
)))
In the current development version of DT (>= 0.1.16), you can use the method selectRows() to clear selections. Please see the section "Manipulate An Existing DataTables Instance" in the documentation.
Here is a possible solution, maybe not the best but it works. It is based on re-create the datatable each time the action button is clicked, so the selected rows are removed.
library(shiny)
library(DT)
runApp(list(
server = function(input, output, session) {
mtcars2 = mtcars[, 1:8]
output$x3 = DT::renderDataTable({
# to create a new datatable each time the reset button is clicked
input$resetSelection
mtcars2
}, rownames = TRUE, server = TRUE
)
# print the selected indices
selection <- reactive ({
input$x3_rows_selected
})
output$x4 = renderPrint({
if (length(selection())) {
cat('These rows were selected:\n\n')
output <- selection()
cat(output, sep = '\n')
}
})
},
ui = shinyUI(fluidPage(
title = 'Select Table Rows',
h1('A Server-side Table'),
fluidRow(
column(9, DT::dataTableOutput('x3')),
column(3, verbatimTextOutput('x4'),
actionButton( 'resetSelection',label = "Click to reset row selection")
) #end of column
)
))
))