eventReactive in shiny doesn't update data - r

In my below example, once run in RStudio, by clicking on the "play" button on the slider, the number of rows displaced gradually increases. But by pausing, and then changing the data set name to iris, then clicking the button "Show" and re-clicking "play", the same animated increase in number of rows does not occur...why? and how do i adjust my code to do so...i.e. let the animation occur with a different data set?
The example below is partially adapted from the eventReactive() function
require(shiny)
if (interactive()) {
ui <- fluidPage(
column(4,
sliderInput('x',label='Num Rows',min=2,max=30,step=1,value=3,animate = TRUE),
textInput('tbl_nm',label='Data Set',value='cars'),
br(),
actionButton("button", "Show")
),
column(8, tableOutput("table"))
)
server <- function(input, output) {
# reactively adjust the number of rows
ll <- eventReactive(input$x,{
input$x
})
# change the data sets after clicking the button
dat <- eventReactive(input$button,{
if(input$tbl_nm=='cars'){
dat <- cars
} else {
dat <- get(input$tbl_nm)
}
return(dat)
})
# Take a reactive dependency on input$button, but
# not on any of the stuff inside the function
df <- eventReactive(input$button, {
yy <- ll()
# choose only the relevant data...
head(dat(),yy)
})
# show the final table
output$table <- renderTable({
if(input$button==0){
# show the first few lines of cars at the begining
head(cars, ll())
} else {
# show the selected data
df()
}
})
}
shinyApp(ui=ui, server=server)
}

The reason this is happening is:
output$table <- renderTable({
if(input$button==0){
# show the first few lines of cars at the begining
head(cars, ll())
} else {
# show the selected data
df()
}
})
Every time a button is pressed, its value (input$button) increments by one. It is only 0 when the app opens. Therefore,
head(cars, ll()) only runs before the button is pressed the first time. Afterwards, input$button increments and its value is 2, 3, 4, ... etc.
ll() is an event reactive that depends on input$x (your slider). Therefore, when your slider updates, or when the play sign is pressed, ll() updates, and your table redisplays.
For every time after the first press, df() runs instead. This is an event reactive that depends on input$button - it only runs when the button is pressed. Your table is prevented from updating until the button is pressed.
To fix this, you can use:
df <- eventReactive(input$button | input$x, {
yy <- ll()
# choose only the relevant data...
head(dat(),yy)
})
as your df() instead. It will now update if the button is pressed or if the slider updates

Related

How can i register a click and at the same time update the click counter table in shiny?

I'm trying to create a project voting app and I need users to vote on the most popular proposals. So I need a click counter on each of the proposals. A example with the iris dataset where Sepal.Length is the record clicked.
library(shiny)
library(reactable)
iris2 = iris #to start
saveRDS(iris2, paste0(getwd(),'/iris.Rds'))
iris2 = readRDS(paste0(getwd(),'/iris2.Rds'))
ui <- fluidPage(
titlePanel("row selection example"),
reactableOutput("table"),
verbatimTextOutput("selected")
)
server <- function(input, output, session) {
selected <- reactive(getReactableState("table", "selected"))
output$table <- renderReactable({
reactable(x(), selection = "single", onClick = "select")
})
observe({
iris2[selected(), "Sepal.Length"] = iris2[selected(), "Sepal.Length"] +1
print(iris2[selected(), ])
saveRDS(iris2,paste0(getwd(),'/iris2.Rds'))
})
x <- reactivePoll(2000, session,
checkFunc = function() {
if (file.exists(paste0(getwd(),'/iris2.Rds')))
file.info(paste0(getwd(),'/iris2.Rds'))$mtime[1]
else
""
},
valueFunc = function() {
iris2=readRDS(paste0(getwd(),'/iris2.Rds'))
}
)
}
shinyApp(ui, server)
My problem is that the app records the vote, but then goes back to the original record and doesn't count the vote. how can i stop shiny from going back to the original record and at the same time update the voting table?
I think you have 2 main problems:
The version you save is not the latest version plus the vote, but you always start from the original iris2, because iris2 is modified only inside the observe bloc. If you want to modify the general variable, you will need to use <<- (or in a more cleaner way maybe a reactive variable)
The observe run once it's updated because the modification reset the selection. You need to stop the observe if the selected is empty (actually it seems cleaner anyway)
You observe becomes like that:
observe({
selection <- selected()
if(!is.null(selection)) {
iris2[selection, "Sepal.Length"] <<- iris2[selection, "Sepal.Length"] + 1
print(iris2[selection, ])
saveRDS(iris2, paste0(getwd(),'/iris2.Rds'))
}
})

How to pass reactive values between observers?

In the code posted at the bottom, I'm trying to cap the value of the second reactive object (y) at the value of the first reactive object (x) using two observeEvents() as action button click counters, as explained in the illustration below. The illustration shows the results of clicking the "Pos" button 3 times and the "Neg" button 4 times. How would this be done?
I commented-out one of my attempts in the below code.
Illustration:
Code:
library(shiny)
ui <- fluidPage(br(),
actionButton("Btn1", "Pos"),
actionButton("Btn2", "Neg"),
br(),br(),
textOutput("posClicks"),
textOutput("negClicks"),
textOutput("netClicks")
)
server <- function(input, output, session) {
x = reactiveVal(0)
y = reactiveVal(0)
observeEvent(input$Btn1,{x(x()+1)})
observeEvent(input$Btn2,{y(y()+1)})
# below is commented-out because it gives strange results counting in leaps of 2's
# observeEvent(input$Btn2,{
# if(x()-y(y()+1) >= 0){y(y()+1)}
# })
output$posClicks <- renderText({paste('Pos clicks =',x())})
output$negClicks <- renderText({paste('Neg clicks =',y())})
output$netClicks <- renderText({paste('Net clicks =',x()-y())})
}
shinyApp(ui, server)
If you really need to have two separate observes, you could do
observeEvent(input$Btn1,{x(x()+1)})
observeEvent(input$Btn2,{y(y()+1)})
observe({
if (y()>x()) {y(x())}
})
Rather than listening for clicks on the button, you just observe the value of y() and if it gets bigger than x(), just reset it. If you can, it would be easier just to change the Btn2 logic
observeEvent(input$Btn1,{x(x()+1)})
observeEvent(input$Btn2,{y(min(y()+1, x()))})

"Next" button in a R Shiny app

I'm trying to build an step by step app using Shiny. My aim is creating an examen consisting in a bunch of questions written in a database. What I need is a "next" button which when you click another question shows up.
I've been triying with an "action button" but it just works the first time, that is, the first time that it is clicked a question shows up, but it becomes unclickable once clicked for first time (it doesn't work as a "next button" as I wish).
Here is the code:
Server.R:
library(xlsx)
data<-read.xlsx("data/base.xlsx",sheetName="Full1")
shinyServer(function(input, output) {
data[,2]<-as.character(data[,2])
question<-data[2,2]
ntext <- eventReactive(input$goButton, {
question
})
output$nText <- renderText({
ntext()
})
})
ui.R:
shinyUI(pageWithSidebar(
headerPanel("Exam"),
sidebarPanel(
actionButton("goButton", "Next"),
p("Next Question")
),
mainPanel(
verbatimTextOutput("nText")
)
))
Thank you so much.
You can do something like this. Please note the comments in the code
rm(list = ls())
library(shiny)
questions <- c("What is your name?","Can you code in R?","Do you find coding fun?","Last Question:How old are you?")
ui <- pageWithSidebar(
headerPanel("Exam"),
sidebarPanel(actionButton("goButton", "Next"),p("Next Question")),
mainPanel(verbatimTextOutput("nText")))
server <- function(input, output,session) {
# Inititating reactive values, these will `reset` for each session
# These are just for counting purposes so we can step through the questions
values <- reactiveValues()
values$count <- 1
# Reactive expression will only be executed when the button is clicked
ntext <- eventReactive(input$goButton,{
# Check if the counter `values$count` are not equal to the length of your questions
# if not then increment quesions by 1 and return that question
# Note that initially the button hasn't been pressed yet so the `ntext()` will not be executed
if(values$count != length(questions)){
values$count <- values$count + 1
return(questions[values$count])
}
else{
# otherwise just return the last quesion
return(questions[length(questions)])
}
})
output$nText <- renderText({
# The `if` statement below is to test if the botton has been clicked or not for the first time,
# recall that the button works as a counter, everytime it is clicked it gets incremented by 1
# The initial value is set to 0 so we just going to return the first question if it hasnt been clicked
if(input$goButton == 0){
return(questions[1])
}
ntext()
})
}
shinyApp(ui = ui, server = server)

R shiny isolate reactive data.frame

I am struggling to understand how isolate() and reactive() should be used in R Shiny.
I want to achieve the following:
Whenever the "Refresh" action button is clicked:
Perform a subset on a data.frame and,
Feed this into my function to recalculate values.
The subset depends on a group of checkboxes that the user has ticked, of which there are approximately 40. I cannot have these checkboxes "fully reactive" because the function takes about 1.5 sec to execute. Instead, I want to give the user a chance to select multiple boxes and only afterwards click a button to (a) subset and (b) call the function again.
To do so, I load the data.frame in the server.R function:
df1 <- readRDS("D:/././df1.RData")
Then I have my main shinyServer function:
shinyServer(function(input, output) {
data_output <- reactive({
df1 <- df1[,df1$Students %in% input$students_selected]
#Here I want to isolate the "students_selected" so that this is only
#executed once the button is clicked
})
output$SAT <- renderTable({
myFunction(df1)
})
}
How about something like
data_output <- eventReactive(input$button, {
df1[,df1$Students %in% input$students_selected]
})
Here is my minimal example.
library(shiny)
ui <- list(sliderInput("num", "rowUpto", min= 1, max = 10, value = 5),
actionButton("btn", "update"),
tableOutput("tbl"))
server <- function(input, output) {
data_output <- eventReactive(input$btn, {
data.frame(id = 1:10, x = 11:20)[seq(input$num), ]
})
output$tbl <- renderTable({
data_output()})
}
runApp(list(ui = ui, server = server))
Edit
Another implementation, a bit more concise.
renderTable by default inspects the changes in all reactive elements within the function (in this case, input$num and input$button).
But, you want it to react only to the button. Hence you need to put the elements to be ignored within the isolate function.
If you omit the isolate function, then the table is updated as soon as the slider is moved.
library(shiny)
ui <- list(sliderInput("num", "rowUpto", min= 1, max = 10, value = 5),
actionButton("btn", "update"),
tableOutput("tbl"))
server <- function(input, output) {
output$tbl <- renderTable({
input$btn
data.frame(id = 1:10, x = 11:20)[seq(isolate(input$num)), ]
})
}
runApp(list(ui = ui, server = server))
Use eventReactive instead:
data_output <- eventReactive(input$updateButton, {
df1 <- df1[,df1$Students %in% input$students_selected] #I think your comments are messed up here, but I'll leave the filtering formatting to you
})
output$SAT <- renderTable({
data_output()
})
And in your UI you should have something like:
actionButton('updateButton',label = "Filter")
Looking at ?shiny::eventReactive:
Use eventReactive to create a calculated value that only updates in
response to an event. This is just like a normal reactive expression
except it ignores all the usual invalidations that come from its
reactive dependencies; it only invalidates in response to the given
event.

Call eventReactive for an arbitrary number of action buttons

What I'm trying to do is create an arbitrary number of action button, each of which has their own event based on their own individual values.
Let's say we want to create a number of buttons. What we do is draw a random number between 1 and 100 and call it n. Then we create n buttons, each with a value between 1 and n (covering every number once). Then, when we press one of those buttons, we render a text message being the number that we pressed.
To set up the buttons, we have:
ui.R
shinyUI(fluidPage(
actionButton('roll','roll'),
uiOutput('buttons')
))
Server.R
shinyServer(function(input, output) {
n <- eventReactive(input$roll, {
num <- sample(1:100,1)
sample(1:num, num, replace=FALSE)
})
output$buttons <- renderUI({
lapply(1:length(n()), function(i) {
actionButton(as.character(n()[i]), as.character(n()[i]) )
})
})
})
This generates the buttons. However, I'm struggling to find a way to create all the necessary eventReactive()s. I tried calling eventReactive() inside a loop, and in a lapply call. However, in order to make that loop or lapply, you need the value of length(n()), which can only be called inside another reactive or observe command.
Given the buttons generated from the above script, how do we make a reactive expression for each button, and then output the text corresponding to the number pressed?
You can search through the input looking for buttons that have been triggered. Once a button is clicked, its value is greater than 0, so all the picked values will print this way (not sure if that is desired?)
library(shiny)
shinyApp(
shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
actionButton('roll','roll'),
uiOutput('buttons')
),
mainPanel(
textOutput('stuff')
)
)
)),
shinyServer(function(input, output) {
n <- eventReactive(input$roll, {
num <- sample(1:100,1)
sample(1:num, num, replace=FALSE)
})
output$buttons <- renderUI({
lapply(1:length(n()), function(i) {
actionButton(as.character(n()[i]), as.character(n()[i]) )
})
})
output$stuff <- renderText({
val <- which(lapply(paste(n()), function(i) input[[i]]) == TRUE)
if (length(val))
sprintf("Picked %s!", paste(n())[val])
})
})
)

Resources