I'm trying to debug my shiny dashboard
For several render* function, I need to debug them with some log (with print or cat) but I can't use those function inside a renderDataTable() / renderText()
for example:
output$selectedData = renderDataTable(
myCsv[which(myCsv[[myCase_id]]==input$process_tokens),]
)
I would like to print something to the console before and after the instruction of renderDataTable() but
output$selectedData = renderDataTable(
cat("rendering...")
myCsv[which(myCsv[[myCase_id]]==input$process_tokens),]
cat("rendered")
)
How can I do this ?
Here is a possible solution to the problem. First I use a variable called data to assingn any calculations to, in your case
data<-myCsv[which(myCsv[[myCase_id]]==input$process_tokens),]. This is used inside the render function and will be created when the output is rendered since it relies on this. I then use an observe function that requires the variable data to be created before printing the second "rendered" to the console. That works once on startup, and will work fine if your data is constant. If you have changing data, for my example the data changes with a user selection, we will have to re-render the table. Since the render function is reactive and you are using input$process_tokens, the render function will re-run when the input changes. In this example it runs when input$select changes. When it runs it resets the variable data to NULL, and we trigger a separate observeEvent that monitors changes to input$select(input$process_tokens). This observeEvent also requires data before continuing, and since the render function set it to null it will not print the second "rendered" until data is created, just as in the first case.
library(shiny)
library(DT)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectizeInput("select","select",choices=(c(1,2,3,4)))
),
mainPanel(
dataTableOutput("selectedData")
)
))
server <- function(input, output, session) {
data<-reactive({data.frame(input$select,4,5)})
output$selectedData <- renderDataTable({
data<-NULL
print("rendering..")
data<-datatable(data())
})
#Observe inital rendering (only needed if no change to data)
observe({
req(data)
print("rendered!")
})
#Observe Changes to data
observeEvent(input$select,{
req(data)
print("rendered!")
})
}
shinyApp(ui, server)
Specific code for you:
server <- function(input, output, session) {
output$selectedData <- renderDataTable({
data<-NULL
print("rendering..")
data<- myCsv[which(myCsv[[myCase_id]]==input$process_tokens),]
})
#Observe inital rendering (only needed if no change to data)
observe({
req(data)
print("rendered!")
})
#Observe Changes to data
observeEvent(input$process_tokens,{
req(data)
print("rendered!")
})
}
shinyApp(ui, server)
Note that you will get two "rendered" printouts when the program initially starts, this is b/c both the observe and observeEvent run since both conditions are met. If your data does change with input$process_tokens, then you can get rid of the observe function, and only use the observeEvent. If your data does not change and the table is only rendered once, then get rid of the observeEvent. I was trying to cover all bases.
Related
I have a dynamic interface that gets created in a huge function. The function spits out a taglist with all the input features and other stuff too. Among this is a DT table. That's all good and works fine. Now I want to be able to replaceData() in the DT, however while creating the taglist i can assign an elementID to the datatable() but the renderDT overwrites that.
If I leave out the renderDT() the table still shows but the replaceData() fails with a
DataTables warning: table id=DataTables_Table_0 - Invalid JSON response.
For more information about this error, please see http://datatables.net/tn/1
error message.
Current working but really bad solution: If I have the renderDT(), some hashed ID is created which starts "out". This can be catched in an observe() and used to create the datatableProxy() object which can then be used to replaceData(). A problem with this is that you can only have one tabel and it is terrible.
there is already an issue on DT git: https://github.com/rstudio/DT/issues/567 but no solution.
library(shiny)
ui <- fluidPage(
uiOutput("inputs")
)
server <- function(input, output, session) {
output$inputs <- renderUI({
tagList( h1("a table has no id")
,renderDT(datatable(mtcars,elementId = "thisDoesHaveAnID"))
,actionButton("replaceDataGo","Replace data go!")
)
})
observeEvent(input$replaceDataGo,{
tableid <- gsub("_.*","",names(input)[grep("out.*",names(input))][1])
tableProxy <- dataTableProxy(tableid,session = session)
replaceData(tableProxy,mtcars[1:input$replaceDataGo,])
})
}
shinyApp(ui, server)
Is there any was to apply an ID maybe already in the taglist to this render?
I am trying to make Shiny App which allows users to save inputs and later load them.
Easiest way to approach this, is to make Save button, which saves inputs. Here is basic app to demonstrate:
server.R
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("integer", "Integer:",
min = 0, max = 1000,
value = 500)
),
mainPanel(tableOutput("values"),
actionButton('save_inputs', 'Save inputs')
)
))
server <- function(input, output, session) {
sliderValues <- reactive({
value = input$integer
})
output$values <- renderTable({
sliderValues()
})
observeEvent(input$save_inputs,{
saveRDS( input$integer , file = 'integer.RDS')
})
}
shinyApp(ui = ui, server = server)
However, I would like to make saving automatic, e.g. I want inputs to be saved at end of session. onSessionEnded() should be answer to this, but it can't reach input values and save them.
session$onSessionEnded( function() {
saveRDS( input$integer, file = 'integer.RDS')
})
Which returns error: Warning:
Error in .getReactiveEnvironment()$currentContext: Operation not
allowed without an active reactive context. (You tried to do something
that can only be done from inside a reactive expression or observer.)
Is there any way to solve it?
Using isolate seems to solve the problem.
session$onSessionEnded(function() {
isolate(saveRDS( input$integer, file = 'integer.RDS'))
})
Using another observe event function and watching the value of isClosed() we can
make this work
observeEvent(session$isClosed()==T,{
saveRDS( input$integer, file = 'integer.RDS')
})
observeEvent() as well as reactive() are both considered "reactive" environments which means they are watching for changing values throughout the session and not just on startup. If you put a function that needs to be reactive outside of a reactive environment shiny will do you the favor of sending you that error, to inform you the function would never be called unless we wrap it in a reactive function.
Also +1 for the well composed question.
I am trying to write a Shiny module which shows a conditionalPanel based on input from the global UI. In the minimal example below the conditionalPanel should show a radioButtons widget when a checkbox in the global UI is clicked, but I can't get it to work.
What am I doing wrong?
library(shiny)
conditional <- function(input, output, session, check){
output$check <- reactive({check()})
outputOptions(output, "check", suspendWhenHidden = FALSE)
output$conditional <- renderUI({
ns <- session$ns
conditionalPanel(
condition = 'output.check',
radioButtons(ns('radioItem'),
'Select option',
choices = c('option 1','option 2'))
)
})
}
conditionalUI <- function(id){
ns <- NS(id)
uiOutput(ns('conditional'))
}
ui <- fluidPage(
fluidRow(checkboxInput('check','Show')),
fluidRow(conditionalUI('mymod'))
)
server <- function(input, output, session) {
check <- reactive({input$check})
callModule(conditional, 'mymod', check = check)
}
shinyApp(ui = ui, server = server)
Simple fix - The condition should be condition = input.check instead of condition = output.check.
You are having a problem with the naming conventions that shiny modules enforce.
Although you have a similar output object in your module, it is not the same as in server. If you specify an output
func <- function(input, output, session) {
output$something <- (...)
}
inside a module, that you called with
callModule(func, 'someIdentifier')
then your output id, which shiny uses to reference all the elements, becomes
someIdentifier-something
You can test this by writing uiOutput("mymod-conditional") instead of uiOutput(ns('conditional')).
Normally, this shouldn't bother you, since modules work the way that all references are resolved within a module. But the conditionalPanel condition, being in JavaScript ("on the other side" so to say), must use global references.
So the fix for your problem would be to change the condition to
condition = 'output["mymod-check"]'
Note that dashes cant be used with JavaScript dot notation, so bracket notation has to be used.
A trick that helped me identify the problem, was to inject JavaScript into the condition in order to show the current value of output on the client side. I placed condition = 'console.log(output)' inside the conditionalPanel so you can inspect the available object in the browser console.
I have the following code in server.R:
library(shiny)
source("helpers.R")
shinyServer(function(input, output) {
output$txtOutput1 <- renderText({
someLengthyComputation(input$txtInput)[1]
})
output$txtOutput2 <- renderText({
someLengthyComputation(input$txtInput)[2]
})
output$txtOutput3 <- renderText({
someLengthyComputation(input$txtInput)[3]
})
})
helpers.R contains the method someLengthyComputation which returns a vector of size 3. How can I get around calling it three times every time txtInput changes and only call it once while updating all three text output controls?
You can simply place someLengthyComputation inside a reactive expression:
shinyServer(function(input, output) {
someExpensiveValue <- reactive({
someLengthyComputation(input$txtInput)
})
output$txtOutput1 <- renderText({
someExpensiveValue()[1]
})
output$txtOutput2 <- renderText({
someExpensiveValue()[2]
})
output$txtOutput3 <- renderText({
someExpensiveValue()[3]
})
})
someLengthyComputation will be triggered only when input$txtInput changes and the first of the outputs is rendered otherwise someExpensiveValue will return a cached value.
It is also possible, although execution strategy is a little bit different, to use a combination of reactiveValues and observe.
If someLengthyComputation is really expensive you should consider adding an action button or a submit button and triggering the computations only when it is clicked, especially when you use textInput.
I am running into an issue because observe is being called first before the UI loads.
Here is my ui.R
sidebarPanel(
selectInput("Desk", "Desk:" , as.matrix(getDesksUI())),
uiOutput("choose_Product"), #this is dynamically created UI
uiOutput("choose_File1"), #this is dynamically created UI
uiOutput("choose_Term1"), #this is dynamically created UI ....
Here is my Server.R
shinyServer(function(input, output,session) {
#this is dynamic UI
output$choose_Product <- renderUI({
selectInput("Product", "Product:", as.list(getProductUI(input$Desk)))
})
#this is dynamic UI
output$choose_File1 <- renderUI({
selectInput("File1", "File 1:", as.list(getFileUI(input$Desk, input$Product)))
})
#this is dynamic UI and I want it to run before the Observe function so the call
# to getTerm1UI(input$Desk, input$Product, input$File1) has non-null parameters
output$choose_Term1 <- renderUI({
print("Rendering UI for TERM")
print(paste(input$Desk," ", input$Product, " ", input$File1,sep=""))
selectInput("Term1", "Term:", getTerm1UI(input$Desk, input$Product, input$File1))
})
This is my observe function and it runs before the input$Product and input$File1 are populated so I get an error because they are both NULL. But I need to use the input from the UI.
observe({
print("in observe")
print(input$Product)
max_plots<-length(getTerm2UI(input$Desk, input$Product, input$File1))
#max_plots<-5
# Call renderPlot for each one. Plots are only actually generated when they
# are visible on the web page.
for (i in 1:max_plots ) {
# Need local so that each item gets its own number. Without it, the value
# of i in the renderPlot() will be the same across all instances, because
# of when the expression is evaluated.
local({
my_i <- i
plotname <- paste("plot", my_i, sep="")
output[[plotname]] <- renderPlot({
plot(1:my_i, 1:my_i,
xlim = c(1, max_plots ),
ylim = c(1, max_plots ),
main = paste("1:", my_i, ". n is ", input$n, sep = "") )
})
})
}##### End FoR Loop
},priority = -1000)
Any idea how to get the input$Product and input$File1 to be populated BEFORE observe runs?
Thank you.
EDIT: Scroll down to TClavelle's answer for a better solution. While this answer has the most upvotes, I wrote it when Shiny had fewer features than it does today.
The simplest way is to add an is.null(input$Product) check at the top of each observe, to prevent it from running before the inputs it uses are initialized.
If you don't want your observers to do the null-check each time they're run, you can also use the suspended = TRUE argument when registering them to prevent them from running; then write a separate observer that performs the check, and when it finds that all inputs are non-null, calls resume() on the suspended observers and suspends itself.
You need to use the Shiny Event Handler and use observeEvent instead of observe. It seems to be about the only way to get rid of the "Unhandled error" message caused by NULL values on app startup. This is so because unlike observe the event handler ignores NULL values by default.
So your observe function could end up looking something like this (no need for priorities, or resume/suspended etc!)
observeEvent(input$Product, ({
max_plots<-length(getTerm2UI(input$Desk, input$Product, input$File1))
... (etc)
})# end of the function to be executed whenever input$Product changes
)
I could not copy paste your example code easily to make it run, so I'm not entirely sure what your full observe function would look like.
You can use req() to "require" an input before a reactive expression executes, as per the Shiny documentation here: https://shiny.rstudio.com/articles/req.html and the function documentation here: https://shiny.rstudio.com/reference/shiny/latest/req.html
e.g.
observeEvent({
req(input$Product)
req(input$File1)
# ...
})
We'd need an MRE to provide a working answer, but, assuming you need input$Product and input$File1, but do not want to take a dependency on them, only on input$Desk, you could:
observe({
product <- isolate(input$Product)
file1 <- isolate(input$File1)
print("in observe")
print(product)
max_plots<-length(getTerm2UI(input$Desk, product, file1))
for (i in 1:max_plots ) {
# ...
}
})
this is probably effectively equivalent to an observeEvent(input$Desk, ....), but might offer more flexibility.