R Shiny Two Outputs for One Action Button - r

I'm trying to use an action button in R Shiny to start a slow-running JAGS model. I would like some text to appear when the user first hits the button that displays what time they pressed the button so that they know something is happening.
So far, the action button is working but it waits until the slow-running model is done to display both the model output and the text.
I have looked at the following questions but they don't seem to answer my question, at least not in a way I understand:
R Shiny execute order
Pattern for triggering a series of Shiny actions
I am new to Shiny so I'm hoping this is a simple problem.
Run.me <- function(a){
# some fake slow function
# This actually takes about 8 hours in real life
for (i in 2:a) {
Foo[i] <<- Foo[i-1] + sample(1:20,1)
}}
library(shiny)
Define server logic ----
server <- function(input, output) {
observeEvent(input$runmodel, {
output$model.running <- renderText({paste("Model started at", Sys.time())})
})
observeEvent(input$runmodel, {
Foo <<- rep(1, 1e6)
Run.me(1e6)
output$model.ran <- renderTable({head(Foo)})
})
}
Define UI ----
ui <- fluidPage(
fluidRow(
column(5, align = "center",
actionButton("runmodel", "Run the Model")),
textOutput("model.running")
),
fluidRow(
column(5, align = "center", tableOutput("model.ran"))
)
)
Run the app ----
shinyApp(ui = ui, server = server)

A possibility, if I correctly understand the question:
server <- function(input, output) {
observeEvent(input$runmodel, {
Foo <<- rep(1, 1e6)
Run.me(1e6)
output$modelran <- renderTable({head(Foo)})
})
}
js <- "
$(document).ready(function() {
$('#runmodel').on('click', function(){
var date = new Date().toLocaleString();
$('#busy').html('Model started: ' + date);
});
$('#modelran').on('shiny:value', function(event) {
$('#busy').html('');
});
});
"
ui <- fluidPage(
tags$head(
tags$script(HTML(js))
),
fluidRow(
column(5, align = "center",
actionButton("runmodel", "Run the Model")),
tags$p(id = "busy")
),
fluidRow(
column(5, align = "center", tableOutput("modelran"))
)
)

In my app which is also building a model slowly I use a progress bar in the server. I know this is not exactly what you are asking for but you might find it an acceptable solution.
modeloutput= reactive(withProgress(message = 'Generating JAGs model', value = 0, {
incProgress(50); generate_jags(params)
}))
output$jags = renderPlot(modeloutput())
I will also follow answers to this question as I would also prefer a solution that has a message or loading bar in the actual plotting window where the output will appear.
I've also found another solution that works by blocking out the action button after it has been clicked and has a small loading bar and completion message. It is available here:
https://github.com/daattali/advanced-shiny/tree/master/busy-indicator

Related

if-statement in Shiny UI: evaluate simple numeric condition calculated in server

In my way to learning Shiny rudiments, I do want to build a simple application that presents a ui with boxes with different background colours based on the value of a variable calculated in server.
I'm trying to use a classical if-statement, being n_add_due_all in server:
fluidRow(
if(n_add_due_all > 0)
{box(title = "to add", background = "red")}
else
{box(title = "to add", background = "green")}
)
I've being trying to use renderUI -> uiOutput and renderPrint -> textOutput server/ui pairs, but I'm not able to get a numeric value suitable for the n_add_due_all > 0 evaluation.
Please, is it doable? Is there any way of simply passing a numeric value from server to ui?
I found numerous related question and answers, but all of them seam much complex user-cases where some kind of interaction with user by selecting or entering values is required. In this case, the variable is completely calculated in server and should only be recalculated upon a page reload.
Thanks for your help!
Are you looking for this?
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
header = dashboardHeader(),
sidebar = dashboardSidebar(),
body = dashboardBody(uiOutput("box"))
)
server <- function(input, output, session) {
n_add_due_all <- -1
output$box <- renderUI({
fluidRow(
if (n_add_due_all > 0) {
shinydashboard::box(title = "to add", background = "red")
} else {
shinydashboard::box(title = "to add", background = "green")
}
)
})
}
shinyApp(ui, server)

R Shiny: how to call a JavaScript function from a custom button inside a Datatable

I have downloaded and would like to incorporate in my R Shiny app the following JavaScript library: GitHub link
As you can see in the snippet below, I have incorporated the sequence-viewer.min.js file in the head of the UI. Additionally, using the tags$div(id="sequence-viewer") I am able to see the corresponding <div> tag in the source of the web page, so up to this point I think everything is fine.
Using the following code, I construct and render a sample Datatable with sequences. From the link of the last column, I would like to dynamically change the value of the var seq=new Sequence(''); with the sequences of the Datatable and draw the result in the sequence-viewer div every time a link is clicked.
library(shiny)
ui <- fluidPage(
theme = shinytheme("yeti"),
useShinyjs(),
useShinyalert(),
tags$head(tags$script(src = "handlebars.js"),
tags$script(src = "sequence-viewer.min.js")),
mainPanel( DT::dataTableOutput("DTtable"),
br(),
tags$div(id="sequence-viewer")
)
)
server <- function(input, output) {
exp1 <- reactive({
tbl <- as.data.frame(c("MALWMPGPGAGSL", "MALKYTFDCVBJUYGFGPGAGSL", "IUYTFVBNMKIUYF"))
names(tbl) <- "Sequence"
tbl$link <- createLink(tbl$Sequence)
return(tbl)
})
createLink <- function(val) {
link <- paste0("<a href='#' onclick='var seq=new Sequence('",val,"'); seq.render('#sequence-viewer');'>Show seq</a>", sep="")
return(link)
}
output$DTtable <- DT::renderDataTable({ exp1()
}, escape = FALSE, options = list(scrollX = TRUE, dom = 'lfrtip', pageLength = 10,
lengthMenu=c(10, 25, 50, 100)), rownames = FALSE)
}
# Run the application
shinyApp(ui = ui, server = server)
I have read many threads and tutorials on how to run custom javascript code in R Shiny, but all the examples that I've found make the call in the ui , not in the server side, could you please advice me how to get the desired output?
NOTE: According to the instructions on github page, the dependencies jquery, handlebars and bootstrap.min.css are required. I suppose that only the handlebars.js has to manually be added given that R Shiny comes already with bootstrap and jquery?
UPDATE 1: Ok, I think I'm now almost there. Thanks to the comments of #YBS I managed to get the idea of how to work with external javascript libraries. The code below works fine if I click the actionLink but it does not work when clicking the custom links inside the Datatable that I create using the createLink function. I get the following exception in the console of the browser: Uncaught ReferenceError: js$seque is not defined. Any ideas why this is happening?
library(shiny)
library(shinyjs)
jsCode = '
shinyjs.seque = function(params) {
var defaultParams = {
seq : "LKJHGFVBJKUGFKGFFGHJKUYTRFGHJUYTGHJIUYT"
};
params = shinyjs.getParams(params, defaultParams);
var seq=new Sequence(params.seq);
seq.render("#sequence-viewer");
}
'
ui <- fluidPage(
useShinyjs(),
extendShinyjs(text = jsCode, functions = c("seque")),
tags$head(tags$script(src = "handlebars.js"),
tags$script(src = "sequence-viewer.min.js")
),
mainPanel( DT::dataTableOutput("DTtable"),
br(),
actionLink(inputId = "action",
label = "action"),
br(),
tags$div(id="sequence-viewer")
)
)
server <- function(input, output) {
exp1 <- reactive({
tbl <- as.data.frame(c("MALWMPGPGAGSL", "MALKYTFDCVBJUYGFGPGAGSL", "IUYTFVBNMKIUYF"))
names(tbl) <- "Sequence"
tbl$link <- createLink(tbl$Sequence)
return(tbl)
})
createLink <- function(val) {
link <- paste0("<a href='#' onclick='js$seque(",val,")' id='",val,"' class='action-button shiny-bound-input'>Show sequence</a>", sep="")
return(link)
}
observeEvent(input$action, {
js$seque("MALKYTFDCVBJUYGFGPGAGSL")
})
output$DTtable <- DT::renderDataTable({
exp1()
}, escape = FALSE, options = list(scrollX = TRUE, dom = 'lfrtip', pageLength = 10,
lengthMenu=c(10, 25, 50, 100)), rownames = FALSE)
}
# Run the application
shinyApp(ui = ui, server = server)
UPDATE 2:
After many hours of debbugging I managed to solve the issue by replacing the onclick='js$seque(",val,")' event of the button in the createLink function with the following: onclick='shinyjs.seque(\"",val,"\")' - or alternatively even clearer onclick='shinyjs.seque(JSON.stringify(val))'
In short, the js$seque call was incorrect at this point, I had to replace this line with shinyjs.seque, namely with the actual name of the function in JS. On the other hand, a typical R actionButton element requires js$seque. I will try to write a clear MRE code and provide it as an answer of this thread.
The usage of shinyjs is an overkill in your case, because you do not want to call the JS function from R but anyways through the client. Thus you can simply use plain JavaScript like in this toy example:
library(shiny)
js <- HTML("seque = function(seq) {
alert(seq);
}")
ui <- fluidPage(tags$head(tags$script(js)),
tags$a(hreg = "#", onclick = "seque('test message')",
"Click me to send test message"))
server <- function(...) {}
shinyApp(ui, server)
Don't get me wrong shinyjs: has its merits, but the typical use case is that you want to trigger JavaScript code from the R side like in this example:
library(shiny)
library(shinyjs)
js_code <- HTML("shinyjs.seque = function(par) {
var def_par = {seq: 'test message'};
par = shinyjs.getParams(par, def_par);
alert(par.seq);
}")
ui <- fluidPage(useShinyjs(),
extendShinyjs(text = js_code, functions = "seque"),
actionButton("click", "Click me to send message"))
server <- function(input, output, session) {
observeEvent(input$click, {
js$seque("test message from R")
})
}
shinyApp(ui, server)
In this example I use shinyjs to call JS directly from R, while in the previous example the JS is called via the onclick.
For your example you could use an actionLink in your table and add an observer and in this you call js$queue but since you will have one link per line this may be tricky (yet not impossible) to code (basically you need dynamic listeners),
Thus, relying on pure JS (onclick) as in your example may be the better option, but then you don't need shinyjs.

Run only relevant observe functions for each tab in shinyapp

In my shiny app I have several tabs as follows.
I have little complex functions running in this app. I think all the observe functions in the server function run when anything is done in any tab. So I need to run only relevant observe functions for relevant tab. As an example, when I am in Summary tab only the relevant observe function should run and all the other observe functions should not run. I have a code.
server <- function(input, output) {
summary <- observe({....})
occupancy<- observe({....})
Bookings<- observe({....})
Maps<- observe({....})
}
Is there any modification to the above code to run only the relevant observe function related to the tab opened in the app.?
Some approaches come to mind. But first; what do you want to do in your observers? If you are simply creating output to display to the user, don't forget to use reactive elements. They will automatically invalidate only when their output is used to display something to the user. Thus if reactive X uses input Y used to construct output for tab A, and input Y changes while we are looking at tab B, reactive X will not invalidate.
If you are using your observers to only create side-effects, such as updating inputs or showing modalDialogs, you could:
use observeEvent instead of observe to only listen to changes in a certain input or condition.
use isolate to make isolate certain dependencies.
build an if-statement in your observer, that checks which tab is selected. You can do that by giving your sidebarMenu an id (my_sidebarmenu in the example below), and check which tab is selected inside your observer by calling input$my_sidebarmenu.
Some examples given below, how this helps~
#UI.R
#loading shiny library
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(
sidebarMenu(id='my_sidebarmenu',
menuItem('Tab 1', tabName='tab1'),
menuItem('Tab 2', tabName='tab2'),
actionButton(inputId = 'btn','Press me!')
)
),
dashboardBody(
tabItems(
tabItem('tab1',
p('Tab 1'),
actionButton('btn_tab1','Show message!')),
tabItem('tab2',
p('Tab 2'),
actionButton('btn_tab2','Show message!'))
)
)
)
server <- function(input, output,session)
{
observeEvent(input$btn,
{
if(input$my_sidebarmenu=='tab1')
{
updateTabItems(session,'my_sidebarmenu',selected='tab2')
}
if(input$my_sidebarmenu=='tab2')
{
updateTabItems(session,'my_sidebarmenu',selected='tab1')
}
})
observeEvent(input$btn_tab1,
{
showModal(modalDialog(
title = "One.",
"You are viewing tab 1!",
easyClose = TRUE,
footer = NULL
))
})
observeEvent(input$btn_tab2,
{
showModal(modalDialog(
title = "Two.",
"You are viewing tab 2!",
easyClose = TRUE,
footer = NULL
))
})
}
shinyApp(ui,server)

R shiny - observeEvent - make the commands execute in order and in real time

My shiny app has to perform some slightly slower server-side calculations so I want the user to be able to keep track of what is happening while they are waiting. Here is a minimal example of the structure of my app:
https://gist.github.com/0bb9efb98b0a5e431a8f
runGist("0bb9efb98b0a5e431a8f")
What I would like to happen is:
Click submit
The app moves to the 'Output' tab panel
It displays the messages and outputs in the order they are listed in observeEvent
What actually happens is:
Click submit
Everything is executed server side at once
The UI is updated at the end
Is it possible to get what I want here?
I could not come up with a solution using your approach. Shiny seems to wait until everything in server = function(input, output) is computed, and displays the results just afterwards, when all components for output$... are available. I don't know if there is a way around that.
There is however a solution implemented, which you could try: Progress indicators
Implementation using your code:
library(shiny)
shinyApp(
ui = navbarPage(title="test", id="mainNavbarPage",
tabPanel("Input", value="tabinput",
numericInput('n', 'Number of obs', 100),
actionButton(inputId="submit_button", label="Submit")
),
tabPanel("Output", value="taboutput",
plotOutput('plot')
)
),
server = function(input, output, session) {
observeEvent(input$submit_button, {
# Move to results page
updateNavbarPage(session, "mainNavbarPage", selected="taboutput")
withProgress(message = "Computing results", detail = "fetching data", value = 0, {
Sys.sleep(3)
incProgress(0.25, detail = "computing results")
# Perform lots of calculations that may take some time
Sys.sleep(4)
incProgress(0.25, detail = "part two")
Sys.sleep(2)
incProgress(0.25, detail = "generating plot")
Sys.sleep(2)
})
output$plot <- renderPlot({hist(runif(input$n)) })
})
})

Passing console output to Shiny reactively as a status update

I have created an application using R and Shiny and want to output everything that happens in the console to a special status window in the Shiny app.
Here is how the skeleton of the function called by Shiny application looks.
myfunction = function(x,y,path....){
if(...){...}
cat("Reading Database\n")
df = read.csv(...)
cat("Processing\n")
#MORE CODE
}
I want a status bar that shows the progress of the called function by viewing the cat(...) console outputs.
If yes, can it be done without making any changes to the original function (making changes only in server.R and/or ui.R)?
Unfortunately I don't know how to do this with the normal Shiny approach of using reactivity. I tried getting it to work with textOuput+printText but I was unable. I'd love to see other solutions, but here is my solution that uses shinyjs package to update the element instead of using reactivity. I hope this works for you, it's pretty simple.
library(shiny)
library(shinyjs)
calculate <- function() {
lapply(1:5, function(x) {
message(x)
Sys.sleep(0.5)
})
message("Done")
}
runApp(shinyApp(
ui = fluidPage(
shinyjs::useShinyjs(), br(),
actionButton("btn","Click me"), br(), br(),
"Progress:",
tags$pre(id = "progress")
),
server = function(input,output, session) {
observeEvent(input$btn, {
withCallingHandlers({
shinyjs::text("progress", "")
calculate()
},
message = function(m) {
shinyjs::text(id = "progress", text = m$message, add = TRUE)
})
})
}
))

Resources