Why does observeEvent don't re-evaluate it's content? - r

In the following example, the text is not shown in the start. If I click on the "show"-Button the text appears. If I then click on the "hide"-Button nothing else happens anymore.
In fact the "textis$visible" variable has always the correct value, but i think the if-statement in the observeEvent funktion is only calculated after the very first button click.
Is there a way to force observeEvent to re-evaluate the if statement? Or are there other ways to stop shiny from executing code in the server part and restart it again (in the real case there would be a whole bunch of function calls inside the if statement, not just hide and show some text)
library(shiny)
ui <- fluidPage(
actionButton(inputId="show","show"),
actionButton(inputId="hide","hide"),
textOutput(outputId = "some_text")
)
server <- function(input, output) {
textis<-reactiveValues(visible=FALSE)
observeEvent(input$show,
textis$visible<-TRUE)
observeEvent(input$hide,
textis$visible<-FALSE)
observeEvent(textis$visible , if(textis$visible){
output$some_text<-renderText({"this is some text"})
})}
shinyApp(ui = ui, server = server)

The observeEvent expressions are evaluated any time the value of their event expression changes. But, in the code you have above, when textis$visible changes, the observer only has instructions to perform if textis$visible is true. In the code snippet below, I've used else{...} to give that observer an action to perform when testis$visible is not true.
observeEvent(textis$visible , if(textis$visible){
output$some_text<-renderText({"this is some text"})
} else {output$some_text<-renderText({''}) }
)}
So, if you paste the else clause above into your app, the output some_text will disappear when the hide button is clicked.

It is not very good practice to put a render element in an observer (and it is unnecessary). Also since you have only one reactiveValue, you could use reactiveVal(), see the example below. You can call its value with text_visible(), and update it with text_visible(new_value).
Working example:
library(shiny)
ui <- fluidPage(
actionButton(inputId="show","show"),
actionButton(inputId="hide","hide"),
textOutput(outputId = "some_text")
)
server <- function(input, output) {
text_visible<-reactiveVal(TRUE)
observeEvent(input$show,
text_visible(TRUE))
observeEvent(input$hide,
text_visible(FALSE))
output$some_text<-renderText({
if(text_visible())
return("this is some text")
else
return("")
})
}
shinyApp(ui = ui, server = server)

try something like this:
library(shiny)
ui <- fluidPage(
actionButton(inputId="show","show"),
actionButton(inputId="hide","hide"),
textOutput(outputId = "some_text")
)
server <- function(input, output) {
textis <- reactiveVal(F)
observeEvent(input$show,{textis(T)})
observeEvent(input$hide,{textis(F)})
result <- eventReactive(textis(),{
if(!textis()){
return()
}
"this is some text"
})
output$some_text<-renderText({result()})
}
shinyApp(ui = ui, server = server)

Related

Why do I get an error message when using an observeEvent with this function that works fine when not wrapped in an observer?

The below example code "Code" saves to the browser the user slider input from one session to the next, using package shinyStorePlus. I would like the user to be able to clear the saved inputs via a click of the "clear" actionButton(). When the commented-out code in "Code" is uncommented, revealing the clear function in the server section, clicking that actionButton() results in error Warning: Error in envir$session$sendCustomMessage: attempt to apply non-function. However, if I pull the clear data code of clearStore(appId = appid) out of the observer and run the code this way, it works fine in clearing out the saved browser data. As an example, running the "Isolated Clearing Code" at the very bottom, completely outside the observer, clears out the browser data like it should.
What am I doing wrong here with my use of an observer? I've fooled around with using isolate(), making the appid reactive, etc., and nothing seems to work.
Code:
library(shiny)
library(shinyStorePlus)
ui <- fluidPage(
initStore(), br(),
sliderInput("input1",label=NULL,min=1,max=200,value=100),
actionButton("clear","Clear data")
)
server <- function(input, output, session) {
appid <- "application001"
setupStorage(
appId = appid,
inputs = list("input1")
)
# observeEvent(input$clear,{
# clearStore(appId = appid)
# })
}
shinyApp(ui, server)
Isolated Clearing Code:
ui <- fluidPage(
initStore(),
)
server <- function(input, output, session) {
appid <- "application001"
clearStore(appId = appid)
}
shinyApp(ui, server)
This seems to be an issue with shinyStorePlus' code:
> clearStore
function (appId)
{
envir <- parent.frame()
envir$session$sendCustomMessage("clearStorage", appId)
}
using parent.frame() to get the session is unfavorable.
Please check the following instead:
library(shiny)
library(shinyStorePlus)
clearStore <- function(appId, session = getDefaultReactiveDomain()){
session$sendCustomMessage("clearStorage", appId)
}
ui <- fluidPage(
initStore(), br(),
sliderInput("input1",label=NULL,min=1,max=200,value=100),
actionButton("clear","Clear data")
)
server <- function(input, output, session) {
appid <- "application001"
setupStorage(
appId = appid,
inputs = list("input1")
)
observeEvent(input$clear,{
clearStore(appId = appid)
})
}
shinyApp(ui, server)
I left a PR here.

shiny reactive output, evaluate input only after first click on actionbutton

I have a simple shiny app where the user should input comma-separated values into a text input, chose the output and click on a button to convert to an output.
I have followed the advice in Update content on server only after I click action button in Shiny to change the output only when clicking, and it works.
However, only when starting/ opening the app the first time, the field is empty, yet the output seems to try to evaluate the input field.
It is more of a cosmetic problem, because once the user filled something in, this does not recur, but I wonder how I could avoid this...
My app:
library(shiny)
ui <- fluidPage(
textInput("from", "csv", value = NULL),
actionButton("run", "Run"),
textOutput("to")
)
server <- function(input, output, session) {
list1 <- reactive({
input$run
x <- isolate(paste(read.table(text = input$from, sep = ",")))
x
})
output$to <- renderText({
list1()
})
}
shinyApp(ui = ui, server = server)
The not-desired output - I would like to get rid of the errors.
you can use req(input$from), see Check for required values

ObserveEvent and textInput, confusing behaviour

Having following simple application:
library("shiny")
ui <- fluidPage(
uiOutput('model'),
textInput('search', 'Look for a model')
)
server <- function(input, output, session) {
options <- c('a', 'b', 'c', 'd')
output$model <- renderUI({
checkboxGroupInput("model",
"Select a model:",
options,
selected='a')
})
observeEvent(input$search,{
updateCheckboxGroupInput(session,"model",
"Select a model:",
options[grep(input$search, options)])
})
}
shinyApp(ui = ui, server = server)
I want to display choices to the user with a already preselected. Later, I would like to update the CheckboxGroupInput only after user starts typing into a search box . However, the checkbox seems to be updated immediately and the preselected choice a is not displayed.
Why does the observeEvent does not wait for interaction with the inputText? Any ideas how I could fix this and wait until user starts typing (I don't want to use button)?
Prevent execution of observe on app load in Shiny provides the answer. The observeEvent will just not be executed on start.
EDIT: (to make answer explicit as #lbusett suggested)
Setting ignoreInit = TRUE in the call to observeEvent solves the problem.

How to refer to reactive element in ui.R in Shiny

I am making an app with drag and drop feature using the ShinyDND package. I would like to pass a list from input as a parameter of dragSetUI, a function that needs to be run in ui.R. I tried renderUI and uiOutput, and it almost works, but the dragged elements fail to be dropped in the drop area. As you can see in the example below, nonreactive choices work like charm. I tried creating a reactive object as well as text output, but I could not find documentation on how to refer to these objects in ui.R. Normally one would refer to output$x as "x" in Output, but here, if I add anything between quotes, it reads as string. I am really stuck with this.
library(shiny)
library(shinyDND)
nonreactive_choices<-as.list(c("a","b","c"))
ui <- shinyUI(
mainPanel(
textInput("choices","Put here a,b,c:"),
dragSetUI("drag", textval = "reactive_choices"),
dragSetUI("drag", textval = "choice_rv"),
textOutput("reactive_choices"),
dragSetUI("drag", textval = nonreactive_choices),
dropUI("drop")
)
)
server = shinyServer(function(input, output,session) {
output$reactive_choices<-reactive({
strsplit(input$choices,",")
})
observe({
chlist<-strsplit(input$choices,",")
choice_rv<-reactiveVal(chlist)
})
})
# Run the application
shinyApp(ui = ui, server = server)
Let's see why the renderUI approach does not work with shinyDND. An app using shinyDND is linked to the dragndrop.js file, which is in the shinyDND folder. In this file one can see:
$(document).ready(function(){
......
$(".dragelement").on("dragstart",function(e){
e.originalEvent.dataTransfer.setData("Text",e.target.id);
});
......
});
This defines the action to perform on elements having class dragelement when a drag is starting, and this is defined when the document is ready. dragSetUI creates such elements.
When you use a dragSetUI inside a renderUI, this creates new elements of class dragelement. But the action defined on such elements in $(document).ready is not effective for these new elements, because this action has been defined just after the document is ready, and then before the effect of renderUI.
A solution consists in defining the action of the event dragstart inside the renderUI. This works:
library(shiny)
library(shinyDND)
nonreactive_choices<-as.list(c("a","b","c"))
ui <- shinyUI(
mainPanel(
textInput("choices","Put here d,e,f:"),
dragSetUI("drag", textval = nonreactive_choices),
uiOutput("dragset"),
dropUI("drop")
)
)
server = shinyServer(function(input, output,session) {
reactive_choices <- reactive({
strsplit(input$choices,",")[[1]]
})
output$dragset <- renderUI({
tagList(
dragSetUI("drag2", textval = as.list(reactive_choices())),
tags$script('$(".dragelement").on("dragstart",function(e){
e.originalEvent.dataTransfer.setData("Text",e.target.id);
});'
)
)
})
})
# Run the application
shinyApp(ui = ui, server = server)
Comment by #ismirsehregal helped me find the solution: shinyjqui can be used for my purposes and it seems to work from inside renderUI. Here is the edited code that does exactly what I needed.
library(shiny)
library(shinyjqui)
ui <- fluidPage(
textInput("choices","Put here a,b,c:"),
uiOutput("reactiveselect"),
orderInput(inputId = 'drop', label = 'Reactive drop', items = NULL,placeholder = "drop here..."),
verbatimTextOutput("droppedorder")
)
server <- function(input, output) {
output$reactiveselect <- renderUI({
req(input$choices)
reactiveitems<- unlist(strsplit(input$choices,","))
orderInput("groupstochoose", "groups to choose from:", connect='drop',items=reactiveitems)
})
output$droppedorder<-
renderPrint({input$drop_order})
}
shinyApp(ui, server)

Multiple inputs triggering same observeEvent not working for button in modalDialog

I have created a minimal code below to reproduce the problem I am facing in my app.
What I am trying to do is call the same observeEvent for multiple inputs where one of the actionButton for observeEvent is present in modalDialog which only gets created after function which is called within the observeEvent is called. The problem I am facing is that after adding this actionButton from modalDialog to call the observeEvent with multiple inputs the observeEvent is never called. If I remove this button the observeEvent is called. The following is my code:
library(shiny)
#Function called from shiny server
func <- function(input,output){
if(is.null(input$txt_Modal)){
output$txt <- renderText("No Text Entered Yet!")
showModal(modalDialog(title = "Choose Survival Time",
textInput(inputId = "txt_Modal", "Enter text:"),
easyClose = FALSE, footer = actionButton(inputId = "btn_Modal_OK","OK")))
}else{
output$txt <- renderText({input$txt_Modal})
}
}
##UI code
ui <- fluidPage(
actionButton(inputId = "btn", label = "Enter function and Print Value"),
textOutput(outputId = "txt")
)
##Server code
server <- function(input, output, session){
observeEvent({
input$btn
input$btn_Modal_OK
},{
func(input, output)
})
}
shinyApp(ui = ui, server = server)
Here input$btn_Modal_OK is the button that is created in the ModalDialog which is created within the function func. On removing input$btn_Modal_OK from the observeEvent the code works as expected.
The cause that I can think of why this is happening is because input$btn_Modal_OK is NULL at the start of the program. One method I can think to remove this error is to write a different observeEvent for input$btn_Modal_OK but my actual code has many lines of code within my observeEvent which I don't want to rewrite in another observeEvent and make the code bulky.
Please note that this is not what I am doing in my actual app, I have just written this code to reproduce the problem. Any help is highly appreciated!
The problem was that input$btn switched from NULL to 0 on initialisation and triggered the modal, but you want func only to be triggered when input$btn is actually pressed, that is when it has a value of 1 or above. That's why this solves your problem:
observeEvent(c(input$btn, input$btn_Modal_OK), {
validate(need(input$btn > 0, ''))
func(input, output)
})

Resources