conditional for multiple events to trigger eventReactive in R Shiny [duplicate] - r

I want two different events to trigger an update of the data being used by various plots / outputs in my app. One is a button being clicked (input$spec_button) and the other is a point on a dot being clicked (mainplot.click$click).
Basically, I want to listed for both at the same time, but I'm not sure how to write the code. Here's what I have now:
in server.R:
data <- eventReactive({mainplot.click$click | input$spec_button}, {
if(input$spec_button){
# get data relevant to the button
} else {
# get data relevant to the point clicked
}
})
But the if-else clause doesn't work
Error in mainplot.click$click | input$spec_button :
operations are possible only for numeric, logical or complex types
--> Is there some sort of action-combiner function I can use for the mainplot.click$click | input$spec_button clause?

I know this is old, but I had the same question. I finally figured it out. You include an expression in braces and simply list the events / reactive objects. My (unsubstantiated) guess is that shiny simply performs the same reactive pointer analysis to this expression block as to a standard reactive block.
observeEvent({
input$spec_button
mainplot.click$click
1
}, { ... } )
EDIT
Updated to handle the case where the last line in the expression returns NULL. Simply return a constant value.

Also:
observeEvent(c(
input$spec_button,
mainplot.click$click
), { ... } )

I've solved this issue with creating a reactive object and use it in event change expression. As below:
xxchange <- reactive({
paste(input$filter , input$term)
})
output$mypotput <- eventReactive( xxchange(), {
...
...
...
} )

Here's the solution I came up with: basically, create an empty reactiveValues data holder, and then modify its values based on two separate observeEvent instances.
data <- reactiveValues()
observeEvent(input$spec_button, {
data$data <- get.focus.spec(input=input, premise=premise,
itemname=input$dropdown.itemname, spec.info=spec.info)
})
observeEvent(mainplot.click$click, {
data$data <- get.focus.spec(input=input, premise=premise, mainplot=mainplot(),
mainplot.click_focus=mainplot.click_focus(),
spec.info=spec.info)
})

This can still be done with eventReactive by putting your actions in a vector.
eventReactive(
c(input$spec_button, mainplot.click$click),
{ ... } )

The idea here is to create a reactive function which will execute the condition you want to pass in observeEvent and then you can pass this reactive function to check the validity of the statement. For instance:
validate_event <- reactive({
# I have used OR condition here, you can use AND also
req(input$spec_button) || req(mainplot.click$click)
})
observeEvent(validate_event(),
{ ... }
)
Keep Coding!

Related

R Shiny - undo callModule

Is there a way to undo callModule? The use case is I have a variable number of modules - the number responds to user selection. Let's say the user chooses 10, then a different 10 - is there a way to remove the original 10? Does it happen automatically? I'm worried that memory is being occupied and not released.
It's a bit tricky creating an reprex, here's a snippet of what I mean though:
observeEvent(
input$people
, {
input$people %>%
walk(
~callModule(people_info_server, .x)
)
}
)
Every time the vector input$people changes, the module with people_info_server gets called on each element of input$people and generates a page of information for each person.
What I'd like to do is something like this:
observeEvent(
input$people
, {
remove_existing_calls(people_info_server) # Not sure how to define this function
input$people %>%
walk(
~callModule(people_info_server, .x)
)
}
)
I suppose you would have to create a "destructor" for your module and make sure that the client of the module (the "main app") calls the destructor at the right time. For example, let us suppose the module returns the destructor as a closure.
library(shiny)
## module definition
module_with_destructor <- function(input, output, session) {
output$plot <- renderPlot({
plot(1:input$n)
})
destructor <- function() {
# add more cleanup logic here
output$plot <- NULL
}
return(destructor)
}
We now need to make sure that the main app executes the destructor when memory should be freed.
## main app (client)
myenv <- new.env()
observeEvent(input$create_module, {
if (is.null(myenv$destructor))
myenv$destructor <- callModule(module_with_destructor, "module_id")
})
observeEvent(input$destroy_module, {
if (!is.null(myenv$destructor))
myenv$destructor()
})
Of course, you will have to implement some extra logic in order to use this idea with a dynamic number of modules. However, in your case you could just create a list that collects all the individual destructors and then iterate over them.

Adding reactive values to a dataframe - Rshiny

At the moment I am attempting the following: import a file in Rshiny, give it a number (interactive), and then move on to the next file. This part works fine. However, I would also like to store the data of every iteration, and then show it on the user interface.
However, it is not working. So I guess something is not right with the reactivity, but I am not sure how to fix it.
ui<-fluidPage(
mainPanel(
radioButtons(inputId="score",label="Give a score",choices=c(1:9),selected=1),
actionButton(inputId="new","Next file"),
tableOutput("savdat")
)
)
server<-function(input,output){
NoFiles<-length(list.files())
Here an empty reactive data.frame
outputdata<-reactive(data.frame("file"="file","score"="score"))
filename<-eventReactive(input$new,{
WhichFile<-sample(1:NoFiles,1)
filename<-list.files()[WhichFile]
return(filename)
})
scores<-eventReactive(input$new,{
return(input$score)
})
Then I would like to append the previous values of the outputdata, with the new values. But it is not working
outputdata<-eventReactive(input$new,{
rbind(outputdata(),filename(),scores())
})
output$savdat<-renderTable(outputdata())
}
shinyApp(ui, server)
Any advice would be welcome
It appears you want the reactivity to occur each time you click on the 'Next file' button. I rewrote your code to respond just once, using 'ObserveEvent', each time the 'Next file' button is clicked. The 2nd challenge is permitting values to persist upon each reactive event. While there are multiple ways to handle this, I chose an expedient technique, the '<<-' assignment statement, to permit the variable 'output data' to persist (this is generally not a good programming technique). Because the variable 'outputdata' exists in all environments, you'll need to wipe your environment each time you want to run this program.
Here's my rewrite using the same ui you created:
ui<-fluidPage(
mainPanel(
radioButtons(inputId="score",label="Give a score",choices=c(1:9),selected=1),
actionButton(inputId="new","Next file"),
tableOutput("savdat")
)
)
server<-function(input,output){
NoFiles<-length(list.files())
setupData <- function(filename,score) {
data <- data.frame(filename,score,stringsAsFactors = FALSE)
return(data)
}
observeEvent (input$new, {
WhichFile<-sample(1:NoFiles,1)
filename<-list.files()[WhichFile]
if (!exists(c('outputdata'))) {
score <- input$score
outputdata <<- data.frame (filename,score,stringsAsFactors = FALSE)
}
else {
outputdata <<- rbind(outputdata,setupData(filename,input$score))
}
# Show the table
output$savdat<-renderTable(outputdata)
})
}
shinyApp(ui, server)

Test whether any input in a set of numbered input objects in R Shiny is empty

Let's say I have created 10 selectInput dropdowns for a multi plot export and these selectInputs are called "xaxis_1", "xaxis_2", ..... , "xaxis_10"
for a single 1 I can write:
if(!is.null(input$xaxis_1)) { .... do stuff } to stop it running export when the user hasn't entered any name, and presses submit, to avoid crashes.
A bit more general you can check this:
if(!is.null(input[[paste('xaxis', i, sep = '_')]])) { ...}
how can you write it elegantly so that 1 line of code checks whether ANY of the 1:10 input[[...]] is empty, i.e. NULL?
The nr of inputs depends on how many plots the user wants to export per file, so all is build with lapply(1:input$nrofplots, function(i) { .... } renderUI structure, and my if statement needs to have the same flexibility of 1:n
In a situation like below in the image, pressing Initiate export should give a sweetalert (got that covered) saying there is at least 1 value missing
Here a snippet I used in the UI side to validate the user's inputs.
library(shiny)
library(shinyjs)
ui <- fluidPage(
useShinyjs(), # Set up shinyjs
numericInput('axis1','Val 1',1),
numericInput('axis2','Val 2',1),
numericInput('axis3','Val 3',1),
actionButton('Go','Plot')
)
server <- function(input, output, session) {
#Try 1, space, AAA and check what shiny will return
observe(print(input$axis1))
observe({
All_Inputs <- vapply(paste0('axis',1:3),
function(x){isTruthy(input[[x]])},
logical(1))
All_InputsCP <- all(All_Inputs)
shinyjs::toggleState(id="Go", condition = All_InputsCP) #This is to make the button Go able or disable according to condition All_InputsCP #
})
}
shinyApp(ui, server)
I hope it helps.

Error in match(el, set, 0L) : 'match' requires vector arguments?

In running the following code in Shiny in R :
client_report_type = reactive({ input$report_type })
if ( is.element(client_report_type,"Enterprise_user"))
...
I encountered the following error message:
Error in match(el, set, 0L) : 'match' requires vector arguments
Does anyone know what does it mean, and how to resolve the problem?
Thanks!
You don't need to put an input inside a reactive to get the value, but the input should be inside of a reactive expression. Anything outside a reactive expression will be execute only once when the shiny app starts. And if you try to use an input value outside a reactive expression there will be an error. Depending of what you are going to do with input$report_type you can put it in a reactive (of course), observe, or observeEvent.
Here are some basic examples:
reactive:
dat <- reactive({
if ( is.element(input$report_type,"Enterprise_user")) {
...
myData
} else {
NULL
}
})
observe:
observe({
if (is.null(input$report_type))
return()
if ( is.element(input$report_type,"Enterprise_user"))
...
})
observeEvent:
observeEvent(input$report_type, {
if ( is.element(input$report_type,"Enterprise_user"))
...
})
Here is great tutorial about shiny and reactivity: http://deanattali.com/blog/building-shiny-apps-tutorial/#reactivity-101

Why doesn't my Shiny (R) actionButton respond after I use a different actionLink?

I'm writing a Shinyapp that enables users, among other things, to input new entries to a mongodb and delete specific rows from it.
I'm trying to add a functionality that would allow to undo the last delete by saving a temporary copy of the row. It seems to work fine, but after I use undo, for some reason the delete button doesn't work anymore, and I can't figure out why.
I thought maybe it has something to do with the fact that there's a few other places where I use observers for the two buttons, but I don't understand why that would cause any problem (and I need them for the app to function properly) - at any rate, they don't prevent me from deleting several rows one after the other so long as I don't use the undo function.
As you can see from the code below, I've put a bunch of print() functions throughout it to try and figure out where it's going. The weird thing - none of them show up! It's like the delete button simply doesn't activate the script once undo was used. Any ideas why?
UPDATE: Here's a short version of server.R and ui.R that reproduces the problem (without using mongodb):
server.R
tempEntry<-NULL
shinyServer(function(input, output, session) {
dat<-data.frame(nums=1:3,ltrs=c("a","b","c"))
## Action: Delete entry
output$delError<-renderText({
input$delButton
isolate({if (!is.na(input$delNum)) {
tempEntry<<-dat[input$delNum,]
output$undo<<-renderUI({
actionLink("undo","Undo last delete")
})
dat<<-dat[-input$delNum,]
print("deleted")
print(dat)
} else print("nope2")
})
})
## Action: Undo delete
output$undoError<-renderText({
input$undo
if (!is.null(input$undo)) {
if (input$undo>0) {
isolate({if (!is.null(tempEntry)) {
dat<<-rbind(dat,tempEntry)
tempEntry<<-NULL
output$delError<<-renderText({""})
print(dat)
} else print("nope3")
}) } else print("undo==0") } else print("undo null")
})
})
ui.R:
library(shiny)
shinyUI(navbarPage("example",
tabPanel("moo",
titlePanel(""),
fluidPage(numericInput("delNum","Row to delete",value=NULL),
actionButton("delButton","Delete row"),
uiOutput("undo"),
div(p(textOutput("delError")),style="color:red"),
div(p(textOutput("undoError")),style="color:blue")
))))
(This also gives an error "argument 1 (type 'list') cannot be handled by 'cat'" after deleting a row, I don't know why... But the problem doesn't seem to be related to that).
Thanks!
That happens because of the output$delError<<-renderText({""}) code that overwrites the original output$delError expression by the empty one, so no surprise output$delError does not trigger on input$delButton any more.
[UPDATE]
The OP's application uses actionButton and actionLink to delete and undelete records from a database, respectively. The 'delete' button is supposed to trigger the delError expression that deletes the record and shows the outcome of deletion (e.g. 'record deleted'). Similarly, the 'undelete' button triggers the undoError expression that puts the record back into the table and reports an outcome of undeletion (e.g. 'record undeleted'). The problem is that undoError has to get rid of the output produced by delError because outputs 'record deleted' and 'record undeleted' don't make much sense when they appear together, but the output 'record deleted' can be removed only by the delError expression.
It seems that this problem can be resolved by modifying delError to make it hide its output when the 'undelete' button (or link) is pressed. But in this case, delError would trigger on both 'delete' and 'undelete' buttons without being able to say which button caused the evaluation, so it would try to delete a record when the 'undelete' button is pressed!
The sample application below provides a way to address this problem by using a global variable that stores the status of the last operation. This status is generated by two high-priority observers (one for 'delete' and another for 'undelete'), which also take care of actual deleting/undeleting of the record. The observers don't produce output that directly goes to the web page, so there is no hassle with getting rid of the messages produced by the other observer. Instead, the status variable is shown by a simple reactive expression.
server.R
tempEntry<-NULL
dat<-data.frame(nums=1:3,ltrs=c("a","b","c"))
shinyServer(function(input, output, session) {
del.status <- NULL
##################
### Observers ####
##################
delete.row <- observe({
if (input$delButton ==0 ) return() # we don't want to delete anything at start
delNum <- isolate( input$delNum ) # this is the only thing that needs to be isolated
if (is.na(delNum)) {
print('nope2')
return()
}
tempEntry <<- dat[delNum,]
dat <<- dat[-delNum,]
output$undo <<- renderUI( actionLink("undo","Undo last delete") )
del.status <<- 'deleted'
},priority=100) # make sure that del.status will be updated *before* the evaluation of output$delError
undelete.row <- observe({
if (is.null(input$undo) || input$undo==0) return() # trigger on undowe don't want to undelete anything at the beginning of the script
dat <<- rbind(dat,tempEntry)
tempEntry <<- NULL
output$undo <<- renderUI("")
del.status <<- 'undeleted'
},priority=100)
##################
### Renderers ####
##################
output$delError <- renderText({
if (input$delButton == 0) return() # show nothing until first deletion
input$undo # trigger on undo
return(del.status)
})
output$show.table <- renderTable({
input$delButton; input$undo # trigger on delete/undelete buttons
return(dat)
})
})
ui.R
library(shiny)
shinyUI(
navbarPage(
"example"
, tabPanel("moo"
, titlePanel("")
, fluidPage(
numericInput("delNum","Row to delete",value=NULL)
, div(p(textOutput("delError")),style="color:red")
, actionButton("delButton","Delete row")
, uiOutput("undo")
, tableOutput('show.table')
)
)
)
)

Resources