Saving time on clicking action button in shiny - shiny

I have created a shiny app to save start time and and end time when I click these buttons. I have three buttons: Start, End and Download, this app is intended to save the time when I have clicked Start and End buttons and then save the file in .csv when I click Download button. But it is saving the time of Download button click. Please help me save the different time properly.
library(shiny)
shinyUI(fluidPage(
titlePanel("Header"),
sidebarLayout(
sidebarPanel(
actionButton("start", "Start"),
tags$br(),
actionButton("end", "End"),
tags$br(),
downloadButton("downloadData", "Download")
),
mainPanel(
)
)
))
shinyServer(function(input, output) {
startTime <- eventReactive(input$start,{
Sys.time()
})
endTime <- eventReactive(input$end,{
Sys.time()
})
data <- reactive({data.frame(start = startTime(),
end = endTime())})
output$downloadData <- downloadHandler(
filename = function() {
"download.csv"
},
content = function(file) {
write.csv(data(), file, row.names = F)
}
)
})

Use global variables would work. Example below (edited to use per-session global variable).
library(shiny)
ui <- shinyUI(fluidPage(
titlePanel("Header"),
sidebarLayout(
sidebarPanel(
actionButton("start", "Start"),
tags$br(),
actionButton("end", "End"),
tags$br(),
downloadButton("downloadData", "Download")
),
mainPanel(
)
)
))
server <- shinyServer(function(input, output) {
starttime <- NULL
endtime <- NULL
observeEvent(input$start, {
starttime <<- Sys.time()
})
observeEvent(input$end, {
endtime <<- Sys.time()
})
output$downloadData <- downloadHandler(
filename = function() {
"download.csv"
},
content = function(file) {
data <- data.frame(start=starttime, end=endtime)
write.csv(data, file, row.names = F)
}
)
})
shinyApp(ui = ui, server = server)

Related

tableHTML in Shiny app - can't handle NULL

I'm trying to output a table via tableHTML that depends on some input in a Shiny app. In the example below, I want the table to depend on the radio button. I'm getting an error saying "Error: no function to return from, jumping to top level", so it seems it doesn't like my two return-statements. Any ideas how to go about this?
library(shiny)
library(tableHTML)
ui = fluidPage(
fluidRow(
radioButtons("radio", label = h3("Data on/off"),
choices = list("On", "Off"),
selected = "Off"),
tableHTML_output("mytable")
)
)
server = function(input, output) {
output$mytable <- render_tableHTML({
if ((input$radio == "On")) {
return(tableHTML(mtcars))
}
else {
return(NULL)
}
})
}
shinyApp(ui, server)
The above works when replacing tableHTML_output by tableOutput and render_tableHTML by renderTable and removing the tableHMTL() function.
It seems a package related issue.
Since we are dealing with plain html, we can use shiny::htmlOutput.
library(shiny)
library(tableHTML)
ui <- fluidPage(
fluidRow(
radioButtons("radio",
label = h3("Data on/off"),
choices = list("On", "Off"),
selected = "On"
),
htmlOutput("mytable")
)
)
server <- function(input, output) {
html_table <- eventReactive(input$radio, {
table <- if (input$radio == "On") {
tableHTML(mtcars)
}
return(table)
})
output$mytable <- renderText(
html_table()
)
}
shinyApp(ui, server)
Another workaround is to have two render_tableHTML inside an observeEvent like this:
library(shiny)
library(tableHTML)
ui <- fluidPage(
fluidRow(
radioButtons("radio",
label = h3("Data on/off"),
choices = list("On", "Off"),
selected = "Off"
),
tableHTML_output("mytable")
)
)
server <- function(input, output) {
observeEvent(input$radio, {
if (input$radio == "On") {
output$mytable <- render_tableHTML({
tableHTML(mtcars)
})
} else {
output$mytable <- render_tableHTML({
NULL
})
}
})
}
shinyApp(ui, server)

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

Display "downloading" message in shiny-app when processing markdown file [duplicate]

Is there a built in Shiny attribute that counts the number of times a downloadButton is clicked? I'm not finding it in the function help or web searches. If there's not a built in method how would I go about counting the clicks. Here's a working example:
data <- matrix(1:20, nrow=5)
ui <- fluidPage(title = 'Count Button Clicks',
fluidRow(style = "padding-bottom: 20px;",
column(width=6,
textOutput("actionclickCount"),
br(),
textOutput("downloadclickCount")
),
column(width=6,
actionButton("actionBtn", "Action Button"),
br(),
downloadButton("dwnldBtn", "Download Button")
)
)
)
server <- function(input, output, session) {
output$actionclickCount <- renderText({
paste('Action Button Clicks =',input$actionBtn)
})
output$downloadclickCount <- renderText({
paste('Download Button Clicks =','what variable goes here?')
})
output$dwnldBtn <- downloadHandler(
filename = 'data.csv',
content = function(file){
write.csv(data, file)
},
contentType = 'csv'
)
}
shinyApp(ui = ui, server = server)
I think there is no build-in method. But you could build it yourself.
You can do this by adding a click listener to the button with javascript:
observe({
if(is.null(input$rnd)){
runjs("
var click = 0;
Shiny.onInputChange('rnd', click)
var dwnldBtn = document.getElementById('dwnldBtn')
dwnldBtn.onclick = function() {click += 1; Shiny.onInputChange('rnd', click)};
")
}
})
The output from Shiny.onInputChange('rnd', click) will be accessible in Shiny via input$rnd.
Edit: For multiple buttons you can use:
observe({
for(btn1 in 1:2){
if(is.null(input[[paste0("rnd", btn1)]])){
runjs(
paste0("
var counter", btn1 ,"= 0;
var dwnldBtn = document.getElementById('", paste0("dwnldBtn", btn1), "')
dwnldBtn.onclick = function() {counter", btn1, " +=1; Shiny.onInputChange('", paste0("rnd", btn1), "', counter", btn1,")};
")
)
}
}
})
For a working example see below:
library(shiny)
library(shinyjs)
data <- matrix(1:20, nrow=5)
ui <- fluidPage(title = 'Count Button Clicks',
useShinyjs(),
fluidRow(style = "padding-bottom: 20px;",
column(width=6,
textOutput("actionclickCount"),
br(),
textOutput("downloadclickCount")
),
column(width=6,
actionButton("actionBtn", "Action Button"),
br(),
downloadButton("dwnldBtn", "Download Button")
)
)
)
server <- function(input, output, session) {
output$actionclickCount <- renderText({
paste('Action Button Clicks =',input$actionBtn)
})
output$downloadclickCount <- renderText({
paste('Download Button Clicks =', input$rnd)
})
output$dwnldBtn <- downloadHandler(
filename = 'data.csv',
content = function(file){
write.csv(data, file)
},
contentType = 'csv'
)
observe({
if(is.null(input$rnd)){
runjs("
var click = 0;
Shiny.onInputChange('rnd', click)
var dwnldBtn = document.getElementById('dwnldBtn')
dwnldBtn.onclick = function() {click += 1; Shiny.onInputChange('rnd', click)};
")
}
})
}
runApp(shinyApp(ui = ui, server = server), launch.browser = TRUE)

R shinyBS popup window

I working on a project where I have to create a form in shiny. I currently have a datatable in the UI which has email in the form of hyperlink. Once the hyperlink is clicked the modal window opens where I have another UI which shows the various fields to be filled. I have a save button here that should update my DB in the backend once the button is clicked.
The problem I am facing is that I am unable to reference each email to that particular modal window and my update query updates all the records in the DB. Is there a way to pass all the record details that has been clicked into the modal window??
What I basically need to know is how to update the record that I have clicked on and for which the pop up window is opened??
I am attaching the UI.R and server.R for use.
enter code here
ui.R
library(shiny)
library(DT)
library(shinyBS)
fluidPage(
fluidRow(
actionButton(inputId = "view",label = "Hi")),
#actionButton(inputId = "savepage1", label = "Save"),
DT::dataTableOutput('my_table'),
bsModal("FormModal", "My Modal", "",textOutput('mytext'),uiOutput("form1"),
actionButton("savepage2","Save"),DT::dataTableOutput("table1"),size = "large")
)
enter code here
server.R
library(shinyBS)
server <- function(session, input, output){
uedata<-c("","Prime","Optimus") ##add source data here
output$form1<-renderUI({
tagList(
column(width=6,selectInput("samplevalue","Select Custom Source*",choices=c("Please select",samplevaluedata))),
column(width=6,textInput("sampletext",label = "Enter Text",value = NULL,placeholder = NULL)))
})
on_click_js = "Shiny.onInputChange('mydata', '%s');
$('#FormModal').modal('show')"
convert_to_link = function(x) {
as.character(tags$a(href = "#", onclick = sprintf(on_click_js,x), x))
}
observeEvent(input$view,{
session$sendCustomMessage(type = "unbinding_table_elements", "my_table")
output$my_table <- DT::renderDataTable({
a=dbGetQuery(hcltcprod,paste0("select name,mobile,email,assignedto from public.tempnew order by 3;"))
a <- data.frame(a,row.names = NULL)
a$email <- sapply(a$email,convert_to_link)
a1 <- datatable(a,
escape = F,
options = list(paging = FALSE, ordering = FALSE, searching = FALSE, rownames = FALSE,
preDrawCallback = JS('function() { Shiny.unbindAll(this.api().table().node());}'),
drawCallback = JS('function() { Shiny.bindAll(this.api().table().node()); } ')))
a1
})
})
observeEvent(input$my_table_cell_clicked, {
print(Sys.time())
})
observe({
if(input$savepage2==0)
return()
isolate({
for(i in 1:nrow(a))
dbGetQuery(hcltcprod,paste0("update public.tempnew set s_text='",input$samplevalue,"',s_value='",input$sampletext,"' where mobile in ('",a$email,"');"))
})
})
}
As your example is connected to database and you didnt provide sample data I will go with mtcars dataset. Building on the example in the link you can view the selected data using the following:
rm(list = ls())
library(DT)
library(shiny)
library(shinyBS)
library(shinyjs)
library(shinydashboard)
# This function will create the buttons for the datatable, they will be unique
shinyInput <- function(FUN, len, id, ...) {inputs <- character(len)
for (i in seq_len(len)) {
inputs[i] <- as.character(FUN(paste0(id, i), ...))}
inputs
}
ui <- dashboardPage(
dashboardHeader(title = "Simple App"),
dashboardSidebar(
sidebarMenu(id = "tabs",
menuItem("Menu Item 1", tabName = "one", icon = icon("dashboard"))
)
),
dashboardBody(
tabItems(
tabItem(tabName = "one",h2("Datatable Modal Popup"),
DT::dataTableOutput('my_table'),uiOutput("popup")
)
)
)
)
server <- function(input, output, session) {
my_data <- reactive({
testdata <- mtcars
as.data.frame(cbind(View = shinyInput(actionButton, nrow(testdata),'button_', label = "View", onclick = 'Shiny.onInputChange(\"select_button\", this.id)' ),testdata))
})
output$my_table <- DT::renderDataTable(my_data(),selection = 'single',options = list(searching = FALSE,pageLength = 10),server = FALSE, escape = FALSE,rownames= FALSE)
# Here I created a reactive to save which row was clicked which can be stored for further analysis
SelectedRow <- eventReactive(input$select_button,{
as.numeric(strsplit(input$select_button, "_")[[1]][2])
})
# This is needed so that the button is clicked once for modal to show, a bug reported here
# https://github.com/ebailey78/shinyBS/issues/57
observeEvent(input$select_button, {
toggleModal(session, "modalExample", "open")
})
DataRow <- eventReactive(input$select_button,{
my_data()[SelectedRow(),2:ncol(my_data())]
})
output$popup <- renderUI({
bsModal("modalExample", paste0("Data for Row Number: ",SelectedRow()), "", size = "large",
column(12,
DT::renderDataTable(DataRow())
)
)
})
}
shinyApp(ui, server)

shiny progress bar appearing prematurely

I am trying to use progress bars (via the command 'withProgress') to monitor the completion of a pipeline that I am running in shiny.
There are 6 progress bars. The pipeline is initiated by the uploading of a file and subsequent clicking of an "actionButton" (inputId=action). However, 3 of the progress bars appear temporarily before I have even uploaded the file. And then when I run the pipeline they appear in the wrong order i.e. the one that should be first comes second etc.
Can anyone tell me why this might be happening and how I can rectify it?
Below is a sample of what the pipeline looks like:
#ui.R
shinyUI(fluidPage(
titlePanel("Uploading Files"),
sidebarLayout(
sidebarPanel(
fileInput('file1', 'Choose CSV File',
accept=c('text/csv',
'text/comma-separated-values,text/plain',
'.csv')),
tags$hr(),
checkboxInput('header', 'Header', TRUE),
radioButtons('sep', 'Separator',
c(Comma=',',
Semicolon=';',
Tab='\t'),
','),
radioButtons('quote', 'Quote',
c(None='',
'Double Quote'='"',
'Single Quote'="'"),
'"')
),
mainPanel(
plotOutput('plot')
)
)
))
#server.R
server <- function(input, output, session) {
read <- reactive({
dataInput <- eventReactive(input$action{
inFile <- input$file1
if (is.null(inFile))
return(NULL)
isolate(file<-read.csv(inFile$datapath, header = input$header,
sep = input$sep))
file
})
file_data_manipulated<-reactive({
withProgress(message = 'Please Wait',
detail = 'This may take a while...', value = 0, {
for (i in 1:15) {
incProgress(1/15)
Sys.sleep(0.25)
}
as.numeric(dataInput())
})
})
output$plot<-renderPlot({
withProgress(message = 'Please Wait',
detail = 'This may take a while...', value = 0, {
for (i in 1:15) {
incProgress(1/15)
Sys.sleep(0.25)
}
plot(file_data_manipulated(), main = "Sample clustering to detect outliers", sub="", xlab="", cex.lab = 1.5, cex.axis = 1.5, cex.main = 2)
abline(h = input$cutoff_filter, col = "red")
#legend("bottomleft", scc$csize>1, pt.bg=unique(node_colors), pch=21)
})
})
I think that your code is incomplete. The progress bars appear before because as soon as the server function is called, all the code inside the reactive function will be executed, you need to provide mechanism to control when to show the progress bars. In this case just checking that the file was correctly uploaded with a if would be enough.
I modified your code to show how to control the reactive functions. Since I don't know how your input file is, I just plot some basic data. Also, I don't know how are you using the read <- reactive({, so just removed it.
library(shiny)
ui <- shinyUI(fluidPage(
titlePanel("Uploading Files"),
sidebarLayout(
sidebarPanel(
fileInput('file1', 'Choose CSV File',
accept=c('text/csv',
'text/comma-separated-values,text/plain',
'.csv')),
tags$hr(),
checkboxInput('header', 'Header', TRUE),
radioButtons('sep', 'Separator',
c(Comma=',',
Semicolon=';',
Tab='\t'),
','),
radioButtons('quote', 'Quote',
c(None='',
'Double Quote'='"',
'Single Quote'="'"),
'"'),
br(),
actionButton('action', 'action')
),
mainPanel(
plotOutput('plot')
)
)
))
server <- function(input, output, session) {
dataInput <- eventReactive(input$action, {
inFile <- input$file1
if (is.null(inFile))
return(NULL)
isolate({
file <- read.csv(inFile$datapath, header = input$header,
sep = input$sep)
})
file
})
file_data_manipulated <- reactive({
input$action
if (is.null(dataInput()))
return(NULL)
withProgress(message = 'Please Wait 1',
detail = 'This may take a while...', value = 0, {
for (i in 1:15) {
incProgress(1/15)
Sys.sleep(0.25)
}
as.numeric(dataInput())
})
})
output$plot <- renderPlot({
input$action
if (is.null(dataInput()))
return(NULL)
withProgress(message = 'Please Wait 2',
detail = 'This may take a while...', value = 0, {
for (i in 1:15) {
incProgress(1/15)
Sys.sleep(0.25)
}
# plot(file_data_manipulated(), main = "Sample clustering to detect outliers",
# sub="", xlab="", cex.lab = 1.5, cex.axis = 1.5, cex.main = 2)
# abline(h = input$cutoff_filter, col = "red")
#legend("bottomleft", scc$csize>1, pt.bg=unique(node_colors), pch=21)
plot(sin, -pi, 2*pi)
})
})
}
runApp(list(ui = ui, server = server))