I've been struggling to find a way to ensure that a piece of long running code is executed after a particular user interface is rendered.
I know this use is not quite what shiny was designed for, but I'm developing a survey-like interface that has a sequence of pages with user inputs on each one. What I would like is that after one specific set of UI elements is displayed, I would like a (sometimes long-running) segment of code to run, but only after the UI has renedred, and with a progress indicator, so that users can be inputting what they need to while the code is running.
I have gotten this to work, but currently it involves a (very hacky!) use of a custom message to the client:
output$page <- renderUI({
div([shiny tags])
})
session$onFlushed(function() {
session$sendCustomMessage(type="grabStimuliList", list(TRUE))
})
I have the message handler:
Shiny.addCustomMessageHandler('grabStimuliList',
function(params){
console.log(params);
Shiny.onInputChange("grabStims", 1);
});
And above this, I have an observe event that acts
observeEvent(input$grabStims, {
if(input$grabStims==1){
progress <- shiny::Progress$new()
progress$set(message = "Generating videos", value = 0)
[long running R code, with progress updating mechanisms]
}
})
This does work, but it feels really hacky and wrong to me. Is there a better way to do this? I've tried putting the long running code along with the progress mechanism in the session$onFlushed({...}) directly, but that gives me the error:
Error in public_bind_env$initialize(...) :
'session' is not a ShinySession object.
It will run if I remove the progress code, but then there's no indication to the user of the progress.
I've attached a minimal (non)working example.
Is there anyway to use the progress object inside of session$onFlushed({...})? Thanks.