1

When I run the below code without observeEvent working, it works fine. Those observeEvents are shown separately, below the last question and above the body of code below. They should be placed in the server section, under the 2 reactive inputs and above the output graph. When I uncomment them and run the app, user-corrected input errors can cause the app to crash despite the use of the validate function. (Validate that inputs into the "Y" column of the input matrix are in increasing sequential order, and that the max period in input column "Y" does not exceed the limit set in slider "X"). My questions are:

  1. How do I trigger observeEvent only when the Action Button is clicked? (That way all validations "should" have passed, the user has had time to fix any input errors without crashing the app). observeEvent is used to save inputs and outputs into objects for further use in other R functions, not shown here.
  2. A secondary question, more important is the above, I'll repost this 2nd question once I figure out the above: [If a user tries entering a number in column Y > than the value in slider X, is there a way to override and input into the matrix (input$yield_inputs) the lesser of those 2 numbers? And vice versa, if someone tries moving the slider X value to an amount < than the max in column Y of the input grid, is there a way to override and use the lower of those 2 values in $input$X? With observeEvent operating, these moves crash the app; I´d rather simply override user inputs and not deal with warnings and instructions for the user to fix the inputs.]

The observeEvents in question:

#observeEvent(yield_data(), {yield.inputs <<- unique(yield_data())})

#observeEvent({vector(periods(),yield_data()[,1],yield_data()[,2])},{yield.outputs <<- unique({vector(periods(),yield_data()[,1],yield_data()[,2])})})

And the code:

library(shiny)
library(shinyMatrix)

m <- function(x) {matrix(c(1,1), 1, 2, dimnames = list(NULL, c("Y", "Z")))} 

matrix.input <- function(x) {
  matrixInput(
    x,
    value = {m()},
    rows = list(extend = TRUE, names = TRUE, editableNames = TRUE),
    cols = list(extend = FALSE, names = TRUE, editableNames = FALSE),
    class = "numeric")
} 

input.validate <- function(x,y) {
  suppressWarnings(
    validate(
      need(x() >= max(y()[,1]), 'Modeled periods (X) must be >= max vector period (Y).'),
      need(min(diff(y()[,1])) > 0, 'Vector input periods (column Y) must be in increasing order.')
    )
  )
}

vector <- function(X,Y,Z){    # X = nbr periods to model (complete time horizon);Y = period for variable Z to change; Z = variable effective in period Y                                        
  a <- rep(NA, X)                                                     
  a[Y] <- Z                                                           
  a[seq_len(min(Y)-1)] <- a[min(Y)]                                   
  if(max(Y) < X){a[seq(max(Y)+1, X, 1)] <- 0}                         
  a <- approx(seq_along(a)[!is.na(a)],a[!is.na(a)], seq_along(a))$y   
  b <- seq(1:X)                                                       
  c <- data.frame(X = b, Z = a)                                       
  return(c)
}

ui <- fluidPage(   
  titlePanel("Vector Generator"),   
  sidebarPanel(
    # Input: specify in a slider the number of periods to model ----
    sliderInput("X", "Modeled periods (X):", min = 1, max = 120, value = 60),
    # Input: matrix grid ----
    width = 6, tags$h5(strong("Yield vector inputs:")),     
    matrix.input("yield_inputs"),
  ), 

  mainPanel(
    h5(strong("Plot of yield inputs:")),
    width = 6,plotOutput("graph")
  ) 
)   

server <- function(input, output, session) {   

  periods <- reactive({input$X})  # Periods to model (X) data capture    
  yield_data <- reactive({input$yield_inputs}) # Matrix input grid capture (columns Y and Z)
  
  output$graph <- renderPlot({
    input.validate(periods,yield_data)

    plot({vector(periods(),yield_data()[,1],yield_data()[,2])}, 
       type="b", 
       main = 'X (vector periods) and Z (interpolated variables)')
    })
}

shinyApp(ui, server)

See code below, now including that troublesome observeEvent that causes app crash when user inputs a value in column Y > slider input X:

library(shiny)
library(shinyMatrix)

m <- function(x) {matrix(c(1,1), 1, 2, dimnames = list(NULL, c("Y", "Z")))} 

matrix.input <- function(x) {
  matrixInput(
    x,
    value = {m()},
    rows = list(extend = TRUE, names = TRUE, editableNames = TRUE),
    cols = list(extend = FALSE, names = TRUE, editableNames = FALSE),
    class = "numeric")
} 

input.validate <- function(x,y) {
  suppressWarnings(
    validate(
      need(x() >= max(y()[,1]), 'Modeled periods (X) must be >= max vector period (Y).'),
      need(min(diff(y()[,1])) > 0, 'Vector input periods (column Y) must be in increasing order.')
    )
  )
}

vector <- function(X,Y,Z){    # X = nbr periods to model (complete time horizon);Y = period for variable Z to change; Z = variable effective in period Y                                        
  a <- rep(NA, X)                                                     
  a[Y] <- Z                                                           
  a[seq_len(min(Y)-1)] <- a[min(Y)]                                   
  if(max(Y) < X){a[seq(max(Y)+1, X, 1)] <- 0}                         
  a <- approx(seq_along(a)[!is.na(a)],a[!is.na(a)], seq_along(a))$y   
  b <- seq(1:X)                                                       
  c <- data.frame(X = b, Z = a)                                       
  return(c)
}

ui <- fluidPage(   
  titlePanel("Vector Generator"),   
  sidebarPanel(
    # Input: specify in a slider the number of periods to model ----
    sliderInput("X", "Modeled periods (X):", min = 1, max = 120, value = 60),
    # Input: matrix grid ----
    width = 6, tags$h5(strong("Yield vector inputs:")),     
    matrix.input("yield_inputs"),
  ), 
  
  actionButton(inputId = "go",label = "Save inputs and vector"),

  mainPanel(
    h5(strong("Plot of yield inputs:")),
    width = 6,plotOutput("graph")
  ) 
)   

server <- function(input, output, session) {   

  periods <- reactive({input$X})  # Periods to model (X) data capture    
  yield_data <- reactive({input$yield_inputs}) # Matrix input grid capture (columns Y and Z)
  
  observeEvent({vector(periods(),yield_data()[,1],yield_data()[,2])}, 
               {yield.outputs <<- unique({vector(periods(),yield_data()[,1],yield_data()[,2])})})
  
  output$graph <- renderPlot({
    input.validate(periods,yield_data)

    plot({vector(periods(),yield_data()[,1],yield_data()[,2])}, 
       type="b", 
       main = 'X (vector periods) and Z (interpolated variables)')
    })
}

shinyApp(ui, server)
4
  • observeEvent(input$go, {...}) will only execute when the actionButton("go",...) is clicked. Commented May 27, 2021 at 20:20
  • I did try that in the above Observe Events, it doesn´t work. Depending on where I put the braces within the observeEvent, following input$go, I get an error of "Error : env must be an environment" or "Error in normalizePath(path.expand(path), winslash, mustWork) : path[1]="Test1.R": The system cannot find the file specified". I tried adding input$go only to this: observeEvent(input$go,yield_data(),{yield.inputs <<- unique(yield_data())}) Commented May 28, 2021 at 14:02
  • Your question: "how do I use Action Button to trigger Observe Event function". Your code: not one reference to input$go. I think your title and/or question are confusing. Further, those errors suggest a lot more is going on. (Not sure if it's related, but ... I've rarely found the use of <<- to be good/right, usually it's a sign that the design/architecture needs to be improved. Its use can make several things a bit more difficult.) Commented May 28, 2021 at 14:10
  • I agree, I should have taken the time to separate the 2 questions into 2 separate posts and simplified the example code to get to the core of my requests. In the 2nd posting of code, under UI I set up the action button with go as input (input$go) but I don´t have it in observeEvent (or anywhere else) under Server because it crashes the App. I need to go through the code some more and perhaps repost a simplified question/example. Commented May 28, 2021 at 16:12

1 Answer 1

0

you could use a submitButton in the user interface:

submitButton("Update page", icon('refresh'), width = '100%'),

This way the variables in Observe will only be updated after the user clicks in this button.

Cheers

Sign up to request clarification or add additional context in comments.

3 Comments

From shiny.rstudio.com/reference/shiny/latest/submitButton.html: "The use of submitButton is generally discouraged in favor of the more versatile actionButton()"
Hi Pedro, I tried the submit button. But I´d like the graph generation to be real-time reactive so the user can see the "build" as he inputs into the matrix grid. I would like the observeEvent to process only when the submit (or action) button is clicked. Observe Event is mean to capture final data for further processing in R. Observe Event is what causes the app to crash when a user inputs a Y value > X. If I comment out Observe Event, the user can correct an input error of Y > X without a crash.
Oh, I got it. Actually I always have problems with actionButton on shiny! :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.