问题
I'm trying to build shinyapp to make a very simple quizz. I have a data.frame with 10 questions, and the user clicks to answer either 0 or 1.
It worked until I tried to implement a timer / countdown so that the next question appears automatically after 5 seconds, using an invalidateLater()
call.
The current question number is stored in a reactiveValues() object, variable i
When I add my observe function, the app stopped working and did not display the question. My code is here:
init = data.frame(question=paste("question", 1:10), correct=c(1,1,1,1,1,0,0,0,0,0), answer=NA, stringsAsFactors = FALSE)
library(shiny); library(shinydashboard)
ui=dashboardPage(dashboardHeader(title = "questionnaire"),dashboardSidebar(),dashboardBody(
fluidRow(box(width = 6, title="how it works..", p("balblabla"))),
fluidRow(
box(width = 12, background = "orange",
fluidRow(
box(width = 12,
verbatimTextOutput("question"),
tags$head(tags$style("#question{font-size: 20px; text-align: left; font-weight: bold;}"))
),
box(width = 12,
actionButton("negative", "answer 0", width = '30%'),
actionButton("positive", "answer 1", width = '30%')
))))))
server <- function(input, output, session) {
vals = reactiveValues(df = init, i=1)
output$question <- renderText({vals$df[vals$i, "question"]})
observe({
invalidateLater(5000)
vals$i <- vals$i + 1
})
observeEvent(input$negative, ignoreInit=TRUE, {
vals$df[vals$i, "answer"] = 1
if (vals$df[vals$i, "correct"]==1){
showNotification("WRONG!", type="error", duration=1, closeButton = FALSE)
} else {
showNotification("CORRECT!", type="message", duration=1, closeButton = FALSE)
}})
observeEvent(input$positive, ignoreInit=TRUE, {
vals$df[vals$i, "answer"] = 1
if (vals$df[vals$i, "correct"]==0){
showNotification("WRONG!", type="error", duration=1, closeButton = FALSE)
} else {
showNotification("CORRECT!", type="message", duration=1, closeButton = FALSE)
}})
}
shinyApp(ui, server)
Can you help me fix this observe statement, i Don't know what's going wrong here.
回答1:
You need to isolate
the increment of the index within this observer:
observe({
invalidateLater(5000)
vals$i <- vals$i + 1
})
, otherwise you'll send it in an infinite loop because a value that it "observes" is changed within the observer itself, constantly triggering re-evaluation irrespective from elapsed time (you can verify this by adding a print(vals$i)
instruction in your current code, at the end of the observer.
So, something like this seems to work:
vals = reactiveValues(df = init, i=0)
output$question <- renderText({vals$df[vals$i, "question"]})
observe({
invalidateLater(5000)
isolate(vals$i <- vals$i + 1)
})
Note that I changed the initialization of i
to zero to avoid skipping a question in initialization.
HTH.
来源:https://stackoverflow.com/questions/54364680/increment-reactivevalue-inside-invalidatelater-observer