Weird problem with `renderDataTable` and `renderUI` with different orders - r

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})
})
}
)
}

Related

How to create a Shiny-app that selects from multiple dfs, edits one and downloads edited data

I try to create a shiny app in which one can choose from different dfs. Then one can edit values in the table. At last I would like to download the edited table.
Each step for itself , edit and download, select and download is no problem. All three together: great despair.
I don't seem to understand how Shiny updates the reactive values and how you can cut it off with isolate.
library(shiny)
library(DT)
ui <- fluidPage(
# App title ----
titlePanel("Downloading Data"),
# Sidebar layout with input and output definitions ----
sidebarLayout(
# Sidebar panel for inputs ----
sidebarPanel(
# Input: Choose dataset ----
selectInput("dataset", "Choose a dataset:",
choices = c("rock", "pressure", "cars"), multiple=T),
actionButton(inputId = "goButton",
label = "Run Report"),
# downloadbutton
downloadButton("downloadData", "Download")
),
# Main panel for displaying outputs ----
mainPanel(
DTOutput("x1")
)
)
)
#df <- cars #if this is taken instead of the first "eventReactive" it works
server <- function(input, output) {
eventReactive({
# Take a dependency on input$goButton
input$goButton
# Use isolate() to avoid dependency on input$obs
df <- isolate(input$dataset)
})
#render the editable DT
output[["x1"]] <- renderDT({
datatable(
df,
selection = "single",
editable = TRUE
)
})
# Creating a DF with the edited info
aniRoi2 <- reactiveVal(df)
#Creating proxy
proxy <- dataTableProxy("x1")
#storing edited df in proxy
observeEvent(input[["x1_cell_edit"]], {
info <- input[["x1_cell_edit"]]
newAniroi2 <-
editData(aniRoi2(), info, proxy, rownames = TRUE, resetPaging = FALSE)
aniRoi2(newAniroi2)
saveRDS(newAniroi2, "data_entry_form.rds") # save rds
})
#download the proxy
output$downloadData <- downloadHandler(
filename = function() {
paste("data-", Sys.Date(), ".csv", sep="")
},
content = function(file) {
write.csv(aniRoi2(), file)
}
)
}
shinyApp(ui, server)
Here I try to select a dataset, that only gets loaded by press of a button. Then it should behave like a normal data.frame or tibble.
If I take out the possibilty of selection of dataframes and call between "ui" and "server" "df <- cars" then it works as intended.
As of now I get the error message:
Listening on http://127.0.0.1:4060
Warnung: Error in is_quosure: Argument "expr" fehlt (ohne Standardwert)
52: is_quosure
51: exprToQuo
50: eventReactive
49: server [#2]
3: runApp
2: print.shiny.appobj
1:
Error in is_quosure(expr) : Argument "expr" fehlt (ohne Standardwert)
Thank you very much, any help would be much appreciated. It feels as if I am so close (but it feels like that since a week).
I also tried this download edited data table gives warning in shiny app it uses observe to wrap the selection. But in the shiny-app I get the familiar "Error 'data' must be 2-dimensional (e.g. data frame or matrix)"
PS: If you would forgive a bonus question: How do you debug shiny? I mean how can you see inside of what is happening, how the environment looks like and which processes are working?
There are some issues with your code. First, input$dataset is a character string with the name of the chosen dataset, not the dataset itself. To get the dataset use e.g. get(input$dataset). Second, the way you use eventReactive is interesting. (; Overall I would go for an observeEvent to init your reactiveVal aniRoi2 with the chosen dataset. Finally I have set multiple=FALSE in your selectInput as choosing multiple df's breaks your code and allowing the user to select multiple dfs makes no sense to me. (I guess that you have done this to get rid of the pre-selected value?? )
library(shiny)
library(DT)
ui <- fluidPage(
titlePanel("Downloading Data"),
sidebarLayout(
sidebarPanel(
selectInput("dataset", "Choose a dataset:",
choices = c("rock", "pressure", "cars"), multiple = FALSE
),
actionButton(
inputId = "goButton",
label = "Run Report"
),
downloadButton("downloadData", "Download")
),
mainPanel(
DTOutput("x1")
)
)
)
server <- function(input, output) {
aniRoi2 <- reactiveVal()
observeEvent(input$goButton, {
aniRoi2(get(input$dataset))
})
output[["x1"]] <- renderDT({
datatable(
aniRoi2(),
selection = "single",
editable = TRUE
)
})
proxy <- dataTableProxy("x1")
observeEvent(input[["x1_cell_edit"]], {
info <- input[["x1_cell_edit"]]
newAniroi2 <-
editData(aniRoi2(), info, proxy, rownames = TRUE, resetPaging = FALSE)
aniRoi2(newAniroi2)
})
output$downloadData <- downloadHandler(
filename = function() {
paste("data-", Sys.Date(), ".csv", sep = "")
},
content = function(file) {
write.csv(aniRoi2(), file)
}
)
}
shinyApp(ui, server)

rHandsonTable module returning NULL when called from another tabPanel

I have an application that utilizes a rHandsonTableOutput (HoT) in a module that utilizes a tabPanel. While I can get other data from this module (e.g. selections made in a selectInput) for some reason I get a NULL when trying to get the HoT data. I made a very simple version of my code to show what I mean.
I've found a HoT data is accessible if I place the module in the same UI element. For some reason, if I put it in another tab in a tabPanel it fails to return the data. My sample code should show that here.
UPDATE - I found the module will work so long as I click on and load the tab with the problematic HoT first! Something about "viewing" the tab first seems to fix this problem. I'd love to find a way to not need to do that. Is this related to rshiny "sessions"?
Below is the code that should reproduce the error. You'll find a simple application with two HoTs. The main tab has one put in via module myModuleUI. There's a second drop down tab that contains a second HoT generated via a second module myModuleTabUI. They're identical except that myModuleTabUI places everything in a tabPanel. If you press the "Add Table Button" it simply adds up the numbers in the tables but will fail when trying to do so for the HoT with ID first_tab (i.e. the HoT found in another tab).
This contains the main shiny app
application.R
library(shiny)
library(rhandsontable)
source('my_modules.R')
ui <- navbarPage("Module Test Tool",
tabsetPanel(id = 'mainTabset',
tabPanel("My Tab",
#This is the HoT that is accessible from a module
h4("Table 1"),
myModuleUI('my_module'),
br(),
br(),
fluidRow(
actionButton("sum_btn", "Add table data"),
br(),
br(),
textOutput('table1_sum'),
textOutput('table2_sum'),
br(),
br()
)
),
navbarMenu("My Module Tabs",
#This is the HoT that is inaccessible from a module
myModuleTabUI('first_tab', 'First')
)
)
)
server <- function(input, output, session) {
#Link logic for tab module
callModule(myModule, 'my_module')
callModule(myModuleTab, 'first_tab')
one_col = rep.int(1,3)
df = data.frame(col1 = one_col,
col2 = one_col,
col3 = one_col)
output$hot <- renderRHandsontable({
rhandsontable(df, stretchH = "none", rowHeaders = NULL)
})
#This button sums up all the rHandsonTable data frames
observeEvent(input$sum_btn, {
#Works just fine when not pulling from the same panel's module
module_data = callModule(getMyModuleData, 'my_module')
module_int= module_data$module_int
module_df = module_data$module_hot
output$table1_sum = renderText({
paste0("Sum of Table 1 is: ", sum(module_df)," | Integer one is: ", module_int)
})
#Fails when pulling a hands on table from another tab
module_tab_data = callModule(getMyModuleTabData, 'first_tab') #<---THIS LINE FAILS
module_tab_int= module_tab_data$module_tab_int
module_tab_df = module_tab_data$module_tab_hot
output$table2_sum = renderText({
paste0("Sum of the table in the 'First' tab is: ", sum(module_tab_df)," | Integer in 'First' tab is: ", module_tab_int)
})
})
}
## Create Shiny app ----
shinyApp(ui, server)
This file contains the modules used in this example:
my_modules.R
#Simple module containing one rHandsontable and a drop down list of integers
myModuleUI <- function(id,tab_name){
ns <- NS(id)
fluidRow(
rHandsontableOutput(ns("module_hot")),
selectInput(ns('module_int_list'),"Integers:",c(1:5), selected = 1)
)
}
#Initializes myModuleUI rHandsonTable with some values
myModule <- function(input, output, session) {
two_col = rep.int(2,3)
df = data.frame(col1 = two_col,
col2 = two_col,
col3 = two_col)
output$module_hot <- renderRHandsontable({
rhandsontable(df, stretchH = "none", rowHeaders = NULL)
})
}
#Returns myModule data for use outside of the module
getMyModuleData <- function(input,output,session){
return (
list(
module_hot = hot_to_r(input$module_hot),
module_int = input$module_int_list
)
)
}
#Simple module that adds the same as MyModuleUI, except in a tabPanel
myModuleTabUI <- function(id,tab_name){
ns <- NS(id)
tabPanel(tab_name,
fluidRow(
rHandsontableOutput(ns("module_tab_hot")),
selectInput(ns('module_tab_int_list'),"Integers:",c(1:5), selected = 1)
)
)
}
#Initializes myModuleTabUI rHandsonTable with some values
myModuleTab <- function(input, output, session){
three_col = rep.int(3,3)
df = data.frame(col1 = three_col,
col2 = three_col,
col3 = three_col)
output$module_tab_hot <- renderRHandsontable({
rhandsontable(df, stretchH = "none", rowHeaders = NULL)
})
}
#Returns MyModuleTab data for use outside of the module
getMyModuleTabData <- function(input,output,session){
return (
list(
module_tab_hot = hot_to_r(input$module_tab_hot), #<---THIS LINE FAILS
module_tab_int = input$module_tab_int_list
)
)
}
Please edit these lines:
#Fails when pulling a hands on table from another tab
module_tab_dat = callModule(getMyModuleTabData, 'first_tab') #<---THIS LINE FAILS
module_tab_int= module_tab_dat$module_tab_int
module_tab_df = module_tab_dat$module_tab_hot
You have:
module_tab_int= module_data$module_tab_int
module_tab_df = module_data$module_tab_hot
But you named the module module_tab_dat. Copy-paste error I assume.
Update
Regarding your update please add this line in your module code:
output$module_tab_hot <- renderRHandsontable({
rhandsontable(df, stretchH = "none", rowHeaders = NULL)
})
# Added line
outputOptions(output, "module_tab_hot", suspendWhenHidden = FALSE)
The aptly named suspendWhenHidden is TRUE by default as is probably correct for most use cases. In this case it must be FALSE. See here

Shiny R observeEvent with Multiple Conditions from selectInput

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.

'Select All' checkbox for Shiny DT::renderDataTable

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))

Reset row selection for DT::renderDataTable() in Shiny R

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
)
))
))

Resources