I have a Shiny UI which launches some long-running server code. I want to update the input controls with some calculated defaults on page load and have this displayed and used immediately (the long-running code triggers on page load or submit button). The start of my server looks like this:
shinyServer(
function(input, output, session) {
observe({
startDate <- weekdaysBefore(Sys.Date(), 5)
endDate <- weekdaysBefore(Sys.Date(), 1)
updateDateInput(session, 'startDate', value = startDate)
updateDateInput(session, 'endDate', value = endDate)
})
# long-running calculations
However, the controls only update after everything else runs. This means the UI displays the initial values of the controls instead of the calculated values above whilst the code is running, the server code runs with the initial values, then when the results are presented the controls update to show the desired values - but not the ones being displayed. The documentation for updateDateInput says:
The input updater functions send a message to the client, telling it
to change the settings of an input object. The messages are collected
and sent after all the observers (including outputs) have finished
running.
I would like to update the controls without this collection and/or page load. How is this possible?
You could initialize your UI with the appropriate values. The initial render will then be with the values you've selected.
ui <-dashboardPage(
dashboardHeader(...),
dashboardSidebar(
dateRangeInput("dates",
"Date range",
start = as.character(Sys.Date()-4),
end = as.character(Sys.Date())),
),
dashboardBody(
.
.
.
)
)
Related
Whenever a user refreshes a page, any UI element that has been interacted with retains its new value instead of reverting to its default.
E.g. if I run the app below, change any of the inputs, then refresh the page, the new values are kept rather than the widgets being restored to 'A'.
I can appreciate this is useful in many situations, but is there a way to stop it from within Shiny, or is it controlled by the browser's cache? A hard refresh (ctrl F5) does indeed reset them back to 'A'.
library(shiny)
ui <- fluidPage(
selectInput("select", "Values", LETTERS[1:5], selected='A'),
radioButtons("radio", "Radio", LETTERS[1:5], selected='A'),
checkboxGroupInput("check", "Checkbox", LETTERS[1:5], selected='A')
)
server <- function(input, output) {
}
shinyApp(ui = ui, server = server)
I cannot really reproduce the problem, here's a GIF of what happens on my system when I refresh the browser (Chrome Version 94.0.4606.71):
Background
I am currently building a „Data-Logger“ - App using R Shiny. I do have an REST - API, which returns a value, that changes over time. My goal is to create an Shiny App, in which an user can click on an actionbutton to start writing the values fetched from the api periodically (e.g. every 60 seconds) to a dataframe. The logging of the data also should be stopped, when the user clicks on another actionbutton.
Problem
My problem is writing a function that starts executing when a button is pressed, executes periodically after that and stops executing when another button is pressed.
Previous Ideas
I previously tried using invalidateLater(), but i could not achieve what i desire.
Can you guys help me out with a clever thought or idea?
Thanks in advance!
This should show how it works. invalidateLater() is the right choice. The start/stop buttons change a reactive expression that determines whether polling is on or off. That way, the reactive RestPoll expression gets notified every time it gets switched on/off and, of course, after 500 ms as long as Running() == TRUE.
library(shiny)
ui <- fluidPage(
actionButton("btnStart", "Start"),
actionButton("btnStop", "Stop"),
textOutput("outTime")
)
server <- function(input, output, session) {
Running <- reactiveVal(FALSE)
observeEvent(input$btnStart, {
Running(TRUE)
})
observeEvent(input$btnStop, {
Running(FALSE)
})
RestPoll <- reactive({
if (Running()) # IS called every time `Running` changes
invalidateLater(500, session)
# Add any REST calls here, process the results
return(Sys.time()) # deliver results
})
output$outTime <- renderText({
req(RestPoll())
})
}
shinyApp(ui, server)
You could also do it with a reactiveTimer but that would also poll and use resources when polling is not required.
I am building a shiny application that will be used by multiple users on a Shiny Open Source Server. Here is a small reprex (from the documentation of shiny)
shinyApp(
ui = fluidPage(
numericInput("n", "n", 1),
plotOutput("plot")
),
server = function(input, output) {
output$plot <- renderPlot( plot(head(cars, input$n)) )
}
)
After running the app, I set the numericInput to 10 and I close the browser. When I open the application again, a new session is started and the value in the numericInput is back at 1. How can I ensure that only one session is active and any user accessing the application, sees this session?
My application is quite large (seperated over several files and modules, with dynamic UI's), so it is not desirable, that every user has to go over all the input fields (>50) every time they start the App.
Is there a simple way for me to create a log-in page for my app? I want to render a ui with multiple tabs (ie. fluidPage(navbarPage(tabPanel("tab1", ...), tabPanel("tab2", ...),...)) conditional upon successful user authentication. I initiate a database connection with an R6 object, if it fails, I print out an "error" and the user can retry entering their credentials. If the connection is successful, I'd like to render the ui (ui2) I specify above.
ui <- fluidPage(fluidRow(column(width=12, textInput('uid', label =
"Enter your user id:"), passwordInput("pwd",
label = "Enter your password:"), actionButton("login",
"Login"), uiOutput("resulting_ui"), offset = 5)))
If you already set up a database that can hold usernames and passwords, you can have users enter their credentials, compare them to the ones in the database, and change a value of a reactiveVal() based on that comparison. The value of that reactiveVal() will control whether you show your UI or not.
For example you can use something like this:
logged <- reactiveVal(value = F)
# change the value of logged() when a user enters correct credentials
output$your_ui <- renderUI({
req(logged())
# your ui here...
})
For a more elaborated example take a look here:
https://github.com/yanirmor/shiny-user-management
Starting Shiny app after password input
This did mostly resolve my question. I just had to make some minor changes to get it to work.
I am building a Shiny application, where I want to stop the (local) server when the client is closed. A simple way to achieve this is to include this in the shinyServer function:
session$onSessionEnded(function() {
stopApp()
})
A downside to this approach is if the user decides to hit refresh, then the app dies.
I have tried various workarounds, using e.g. reactiveTimer/invalidateLater to check for connections at certain intervals. However, these take a session reference (they are specific to the session), and so nothing will execute after onSessionEnded.
Is there a way to have a "global" server timer that executes regularly, and coould check for active connections? Or another way to achieve automatic application shut-down but which allows for a refresh of the page?
You could add an actionButton and some code on the server to stop the app when the button is clicked. For example:
runApp(list(
ui = bootstrapPage(
actionButton('close', "Close app")
),
server = function(input, output) {
observe({
if (input$close > 0) stopApp()
})
}
))
However, this won't automatically close the browser window (unless you're viewing with RStudio's built-in browser window). To do that, you need to add some Javascript to the actionButton.
runApp(list(
ui = bootstrapPage(
tags$button(
id = 'close',
type = "button",
class = "btn action-button",
onclick = "setTimeout(function(){window.close();},500);",
"Close window"
)
),
server = function(input, output) {
observe({
if (input$close > 0) stopApp()
})
}
))
Of course, this won't stop the app when the user closes the window some other way. I believe it's also possible to detect window close events in the browser, and then you may be able to set an input value (which goes to the server) at that time, but I don't know whether it'll get to the server before the window is closed and the Javascript stops running.