How do I change the select capability on all columns using DT table in Shiny App? - shiny

I want to have the select capability only on the second column using DT Package in shiny apps.
In this post this question was asked
Using the above solution in DT package in shiny is not working for me. I expect by the code below that clicking on the second column, only, row will be selected.
Any hints on how I use the selector correctly?
data <- data.frame(
a = 1:10,
b = letters[1:10]
)
ui <- fluidPage(
DT::DTOutput("table")
)
server <- function(input, output, session) {
output$table <- DT::renderDT({
DT::datatable(
data = data,
options = list(
select = list(
style = "os",
selector = 'tr>td:nth-child(2)'
)
)
)
}, server = F)
}
shinyApp(ui, server)

The selector is alright, the functionalities to enable a "better" selection are contained in the Select extension. As documented in the extensions page, you need:
Enable the Select extension
Turn off DT’s own select functionality:
data <- data.frame(
a = 1:10,
b = letters[1:10]
)
ui <- fluidPage(
DT::DTOutput("table")
)
server <- function(input, output, session) {
output$table <- DT::renderDT({
DT::datatable(
data = data,
options = list(
select = list(
style = "os",
selector = 'tr>td:nth-child(2)'
)
),
extensions = c("Select"),
selection = 'none'
)
}, server = F)
}
shinyApp(ui, server)

Related

Retrieving unique id and input values from datatable in Shiny

I am attempting to use DT in R to create a table for user review with an appended column (new_column) including a selectInput drop-down that allows them to designate the event as either keep or delete. I am stuck on how to retrieve both the unique id and the user input from the datatable to be able to apply the change to a separate dataframe in Shiny. I would like for the user to push a button ('submit') for the app to know when to capture the values -- with this am I able to circumvent rerending the table or is that a necessary part of capturing the value?
I have worked with Shiny quite a bit but am not super familiar with javascript or datatables in general so have been stuck on this for some time and would appreciate any pointers.
Here is a simplified version of my data and code:
library(DT)
library(tidyverse)
library(shiny)
ui <- fluidPage(
DTOutput('myTableOutput'),
br(),
actionButton("submit", "Apply Changes"))
server <- function(input, output, session) {
for (i in 1:nrow(df)) {
df$new_column[i] <- as.character(selectInput(inputId = df$unique_id[i], label=NULL, choices = c('keep'=TRUE, 'delete'=FALSE)))
}
output$myTableOutput <- DT::renderDataTable({
datatable(
df,
escape = FALSE,
filter = "none",
editable = 'new_column',
selection = "none",
options = list(
dom = "t",
initComplete = JS(
"function(settings, json) {",
"$(this.api().table().header()).css({'background-color': 'DimGray', 'color': 'white'});",
"}"),
drawCallback = JS('function() { Shiny.bindAll(this.api().table().node()); } '))
)
}, server = FALSE)
observeEvent(input$submit,{
#need to retrieve changes (preferably in a df) so I can apply them to another dataframe
reviewed_table <- as.data.frame(input$mytable_cell_edit)
})
}
You can add reactive value (lgl_vec) that has values of all selectInput's inside your table:
server <- function(input, output, session) {
rv <- reactiveValues()
df[["new_column"]] <- map_chr(
map(
df[["unique_id"]],
selectInput,
label = NULL,
choices = c(keep = TRUE, delete = FALSE)
),
as.character
)
output$myTableOutput <- DT::renderDataTable({
datatable(
df,
escape = FALSE,
filter = "none",
selection = "none",
options = list(
dom = "t",
preDrawCallback =JS(
'function(){Shiny.unbindAll(this.api().table().node());}'
),
drawCallback = JS(
'function(){Shiny.bindAll(this.api().table().node());}'
)
)
)
}
)
observeEvent(input$submit, {
lgl_vec <- as.logical(map_chr(df[["unique_id"]], ~input[[.x]]))
rv$reviewed_table <- df[lgl_vec, -ncol(df), drop = FALSE]
print(rv$reviewed_table)
})
}

R Shiny: Updating proxy table column headers in ObserveEvent

I would like to update column headers in an R Shiny proxy table. The app should:
Launch with original column header names (e.g. "Do","Re","Mi","Fa","So")
Change those column headers in the proxy table to something else when the user clicks an action button (e.g. "y1","y2","y3","y4","y5")
Shiny has a convenient updateCaption() method that allows for a similar behavior for proxy table captions. I'd like to do something similar with table column headers for proxy tables. Here's my attempt.
library(shiny)
library(DT)
ui <- fluidPage(
fluidRow(
actionButton(
"updatebutton",
label = "Update Table",
style = "margin-right: 5px;"
),
DT::dataTableOutput("myplot")
),
)
server <- function(input, output) {
mycolumnnames <-c("Do","Re","Mi","Fa","So")
myothercolumnnames <- c("y1","y2","y3","y4","y5")
output$myplot <- DT::renderDataTable({
DF <- data.frame(replicate(5, sample(rnorm(5), 10, rep = TRUE)))
datatable(DF, colnames = mycolumnnames,
caption="Original caption")
})
proxy <- DT::dataTableProxy("myplot")
observeEvent(input$updatebutton, {
updateCaption(proxy, caption="Look, I am a NEW caption!")
DF <- data.frame(replicate(5, sample(rnorm(5), 10, rep = TRUE)))
# names(DF) <- myothercolumnnames # This doesn't work
proxy %>% replaceData(DF)
})
}
shinyApp(ui = ui, server = server)
Edit1: Now uses dataTableProxy()
I took away all the things related to color background so I could focus on your problem.
First, I declare some values outside shiny: your data.frame and two vectors for the column names. Then I assign the column names as the first vector.
Inside the app, I retrieve the data as a reactiveVal(), and update its colnames whenever the button is pressed
library(shiny)
library(DT)
mycolumnnames <-c("Do","Re","Mi","Fa","So")
myothercolumnnames <- c("y1","y2","y3","y4","y5")
DF <- data.frame(replicate(5, sample(rnorm(5), 10, rep = TRUE)))
colnames(DF) <- mycolumnnames
ui <- fluidPage(
fluidRow(
actionButton(
"updatebutton",
label = "Update Table",
style = "margin-right: 5px;"
),
DT::dataTableOutput("myplot")
),
)
server <- function(input, output) {
df <- reactiveVal(DF)
output$myplot <- DT::renderDataTable({
datatable(df(), caption="Original caption")
})
observeEvent(input$updatebutton, {
new_data <- data.frame(replicate(5, sample(rnorm(5), 10, rep = TRUE)))
if(!input$updatebutton %% 2 == 0 ){
colnames(new_data) <- myothercolumnnames
} else {
colnames(new_data) <- mycolumnnames
}
df(new_data)
proxy1 <- DT::dataTableProxy("myplot")
updateCaption(proxy1, caption="Look, I am a NEW caption!")
replaceData(proxy1, df())
})
}
shinyApp(ui = ui, server = server)
So whenever you press the button, the colnames are changed between the two vectors.

Is there a good way to merge data based on a drop down menu in R?

I have been trying to merge data with another data set based on input from a drop down. I have just started learning R and have run into some problems and want to know if there is a better way of going about this.
I am getting an error that it cannot coerce class c(ReactiveExpr, reactive) to a data frame.
library(shiny)
library(plyr)
library(dplyr)
library(xlsx)
server <- function(input, output){
annotation1 <- read.xlsx("input1.xlsx", sheetIndex = 1, header = TRUE)
annotation2 <- read.xlsx("input2.xlsx", sheetIndex = 1, header = TRUE)
data_input <- eventReactive(input$userfile, {
df <- read.xlsx(input$userfile$datapath, sheetIndex = 1, header = TRUE)
})
output$data_input <- renderTable(data_input())
output$annotation <- renderTable(annotation)
data_species <- c("Set1", "Set2")
# Drop-down selection box for which data set
output$choose_species <- renderUI ({
selectInput("species", "Species", as.list(data_species))
})
output$mergeddata <- renderTable({
if(input$species == "Set1"){
eventReactive("Set1",({left_join(data_input(), annotation1, by = c("Column1" = "Column1"))}))
}
else if(input$species == "Set2"){
eventReactive("Set2",({left_join(data_input(), annotation2, by = c("Column1" = "Column1"))}))
}
})
}
ui <- fluidPage(
titlePanel(
div("Test")
),
sidebarLayout(
sidebarPanel(
fileInput("userfile", "Input File", multiple =FALSE,
buttonLabel = "Browse Files", placeholder = "Select File"),
uiOutput("choose_species"),
uiOutput("choose_annotations"),
),
mainPanel(
tableOutput("mergeddata"),
br()
),
),
)
# Run the application
shinyApp(ui = ui, server = server)
In general, you approach seems ok. The error you get is from the line
eventReactive("Set1",({left_join(data_input(), annotation1, by = c("Column1" = "Column1"))}))
An eventReactive returns an (unevaluated) reactive expression which you try to render as data.frame with renderTable. To circumvent this, you could use:
eventReactive("Set1",({left_join(data_input(), annotation1, by = c("Column1" = "Column1"))}))()
However, here you don't need eventReactive, because your reactivity comes from input$species (you want to change the table output based on this input). Therefore, you can just use:
output$mergeddata <- renderTable({
if(input$species == "Set1"){
merge_data <- annotation1
} else {
merge_data <- annotation2
}
left_join(data_input(), merge_data, by = c("Column1"))
})

Save DT table with additional information (Shiny)

I was wondering if it is possible to save DT table content together with some additional information which is not part of the data frame/table like app version number, date of execution, sliderInput value etc.
Thank you!
Reprex below:
library(shiny)
library(DT)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput(inputId = "range", "Set range", 1, 10, 5, 1)
),
mainPanel(
DT::dataTableOutput("table")
)
)
)
server <- function(input, output) {
dfr <- data.frame(var1 <- c(1,2,3),
var2 <- c(11, 22, 33))
output$table <- DT::renderDataTable(
datatable(dfr, extensions = 'Buttons',
class="cell-border stripe",
rownames = FALSE, colnames = c("var1", "var2"),
options = list(dom = "Blfrtip",
buttond = list("copy", list(extend = "collection",
buttons = c("csv", "excel", "pdf"),
text = "Download")), pageLength=10, autoWidth = TRUE,
searchHighlight = TRUE, filter = "top"))
)
}
shinyApp(ui = ui, server = server)
You could save the contents of the data frame and the other information in a list and then save the list.
Or, any R object can have attributes which are completely arbitrary and under your control. You could set attributes of the data frame to record the information you want.
Personally, I'd use the list approach, purely because I don't like attributes.
Here's a suggestion in response to OP's request below.
library(shiny)
library(DT)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput(inputId = "range", "Set range", 1, 10, 5, 1),
actionButton("saveRds", "Save to Rds"),
actionButton("loadRds", "Load from Rds")
),
mainPanel(
DT::dataTableOutput("table"),
wellPanel(h4("Current data"), verbatimTextOutput("text")),
wellPanel(h4("File data"), verbatimTextOutput("loadedData"))
)
)
)
server <- function(input, output) {
dfr <- data.frame(var1 <- c(1,2,3),
var2 <- c(11, 22, 33))
output$table <- DT::renderDataTable(
datatable(dfr, extensions = 'Buttons',
class="cell-border stripe",
rownames = FALSE, colnames = c("var1", "var2"),
options = list(dom = "Blfrtip",
buttond = list("copy", list(extend = "collection",
buttons = c("csv", "excel", "pdf"),
text = "Download")), pageLength=10, autoWidth = TRUE,
searchHighlight = TRUE, filter = "top"))
)
listInfo <- reactive({
list("data"=dfr, "version"="WebApp Version 1.0", "runDate"=date(), "sliderValue"=input$range)
})
output$text <- renderPrint({
listInfo()
})
observeEvent(input$saveRds, {
saveRDS(listInfo(), "data.Rds")
})
fileData <- reactive({
req(input$loadRds)
readRDS("data.Rds")
})
output$loadedData <- renderPrint({
fileData()
})
}
shinyApp(ui = ui, server = server)
The way you implement "save to file" will depend on the file format: Excel files will clearly have different requirements to PDF files, for example. As a minimum effort demonstation, I've created "Save to Rds" and "Load from RDS" buttons in the sidebar and added a verbatimTextOutput to display the contents of the file when it's loaded. [I'm not sufficiently familiar with DT to know how to add the buttons in the table toolbar.]
OP's effort was pretty close: it's just that writing a list to CSV file takes a little more effort than just calling write.csv...

R shiny ColVis and datatable search

I have TableTools and ColVis working together, but, as it was explained in another post (R shiny DataTables ColVis behavior), when clicking the Show/hide columns button, the list mixes up with the values in the table underneath, and I cannot make the list disappear.
In that post is mentioned that shiny atm is not compatible with the current data.table version, and I'd like to know if there's any other solution around. Here's my code:
ui.R
library(shiny)
library(shinythemes)
library(ggplot2)
addResourcePath('datatables','\\Users\\Ser\\Downloads\\DataTables-1.10.7\\DataTables-1.10.7\\media')
addResourcePath('tabletools','\\Users\\Ser\\Downloads\\TableTools-2.2.4\\TableTools-2.2.4')
shinyUI(fluidPage(theme = shinytheme("Journal"),
tags$head(
tags$style(HTML("
#import url('//fonts.googleapis.com/css?family=Lobster|Cabin:400,700');
"))
),
headerPanel(
h1("List Manager",
style = "font-family: 'Lobster', cursive;
font-weight: 500; line-height: 1.1;
color: #ad1d28;")),
sidebarLayout(
sidebarPanel(
#File Upload Manager
fileInput('file1', 'Choose file to upload'),
tagList(
singleton(tags$head(tags$script(src='//cdnjs.cloudflare.com/ajax/libs/datatables/1.10.7/js/jquery.dataTables.min.js',type='text/javascript'))),
singleton(tags$head(tags$script(src='//cdnjs.cloudflare.com/ajax/libs/datatables-tabletools/2.1.5/js/TableTools.min.js',type='text/javascript'))),
singleton(tags$head(tags$script(src='//cdnjs.cloudflare.com/ajax/libs/datatables-tabletools/2.1.5/js/ZeroClipboard.min.js',type='text/javascript'))),
singleton(tags$head(tags$link(href='//cdnjs.cloudflare.com/ajax/libs/datatables-tabletools/2.1.5/css/TableTools.min.css',rel='stylesheet',type='text/css'))),
singleton(tags$head(tags$script(src='//cdn.datatables.net/colvis/1.1.0/js/dataTables.colVis.min.js',type='text/javascript'))),
singleton(tags$script(HTML("if (window.innerHeight < 400) alert('Screen too small');")))
)),
mainPanel(
dataTableOutput("mytable"))
)))
server.R
shinyServer(function(input, output) {
output$mytable = renderDataTable({
inFile <- input$file1
if (is.null(inFile))
return(NULL)
read.table(inFile$datapath, header=TRUE, sep='')
}, options = list(
"dom" = 'TC<"clear">lfrtip',
"colVis" = list(
"activate"="click",
"align"="right"),
"oTableTools" = list(
"sSwfPath" = "//cdnjs.cloudflare.com/ajax/libs/datatables-tabletools/2.1.5/swf/copy_csv_xls.swf",
"aButtons" = list(
"copy",
"print",
list("sExtends" = "collection",
"sButtonText" = "Save",
"aButtons" = c("csv","xls")
)
)
)
)
)
})
I also have another question: I'd like to search "<" or ">" values in the searchboxes at the bottom of the table, but I can't make it work. I don't know if I have to add anything to the code so it can be done (such as "regex" or similar).
You may try the DT package instead of hacking the JS libraries by yourself. Here is a minimal example:
library(shiny)
library(DT)
library(shinythemes)
shinyApp(
ui = fluidPage(
theme = shinytheme('journal'),
fluidRow(column(12, DT::dataTableOutput('foo')))
),
server = function(input, output) {
output$foo = DT::renderDataTable(
iris,
filter = 'bottom',
extensions = list(ColVis = list(activate= "click", align = "right")),
options = list(dom = 'C<"clear">lfrtip')
)
}
)
See http://rstudio.github.io/DT/extensions.html for more info on DataTables extensions.