3

I'm interested in developing a Shiny App that would act in the following manner:

  1. Update the chart following any changes to the number of user interface elements. This done through the actionButton / isolate construct.
  2. Display a warning text prompting user to click the Update button. Outstanding.
  3. Remove the warning text after the Update button was pressed. Outstanding.

Example

Approach

  • Drawing from one of the examples available via the following example provides access to two functionalities that modify the histogram.
  • Histogram is updated upon the Update button click.
  • Upon interaction with the UI the observeEvent construct should update the warning message that is then passed to renderText.
  • If there is no interaction with the UI the renderText can return empty string.
  • actionButton should revert warning message string value to empty c(""). Alternative approach making use from insertUI / renderUI is acceptable.

Code

app.R

library(shiny)

ui <- fluidPage(titlePanel("Reactive UI"),
                sidebarLayout(
                    sidebarPanel(
                        # Reactive text should appear upon any change here
                        sliderInput(
                            "bins",
                            "Number of bins:",
                            min = 1,
                            max = 50,
                            value = 30
                        ),
                        checkboxInput(
                            inputId = "chckBxLg",
                            label = "Log scale",
                            value = FALSE
                        )
                        ,
                        actionButton("btnUpdate", "Update")
                    ),
                    mainPanel(textOutput("msgTxt"),
                              plotOutput("distPlot"))
                ))

# Define server logic required to draw a histogram
server <- function(input, output) {
    # Create placeholder object for msg
    msgUpdateText <- c("")

    # Insert update message text upon change to UI elements
    observeEvent(eventExpr = {
        input$bins
        input$chckBxLg
    },
    handlerExpr = {
        # This message should only show after interaction with the UI
        isolate(msgUpdateText <<-
                    c("You have clicked something, update the chart"))
    })

    # Render text
    output$msgTxt <- renderText(msgUpdateText)

    output$distPlot <- renderPlot({
        input$btnUpdate
        isolate({
            x    <- faithful[, 2]
            if (input$chckBxLg) {
                x <- log(x)
            }
            bins <-
                seq(min(x), max(x), length.out = input$bins + 1)

            # Also delete the text message
            msgUpdateText <<- c("")
        })

        hist(x,
             breaks = bins,
             col = 'darkgray',
             border = 'white')
    })
}

shinyApp(ui = ui, server = server)

Problem

The message:

You have clicked something, update the chart

should only appear only after user interacts with the UI and disappear after the actionButton is pressed, instead the message is visible permanently.

App preview


Side notes

The offered solution should be extensible across a number of UI, elements. The provided, not-working, example attempts to capture change to two UI elements:

observeEvent(eventExpr = {
    input$bins      # Element 1
    input$chckBxLg  # Element 2
},
handlerExpr = {
    # This message should only show after interaction with the UI
    isolate(msgUpdateText <<-
                c("You have clicked something, update the chart"))
})

I'm striving for the code to accommodate a vast number of elements, on the lines

observeEvent(eventExpr = {
    input$bins      # Element 1
    input$chckBxLg  # Element 2
    input$title     # Element 3
    input$n         # Element n
    ...             ...
},
handlerExpr = {
    # User interacted with any of the UI elements listed above
    # Update text message to be displayed in the app
})
Konrad
  • 17,740
  • 16
  • 106
  • 167

1 Answers1

3

I think I've managed to achieve the desired result. I've brought the necessarily logic into 3 observeEvent calls. The first observes any of the inputs changing and sets a variable to TRUE. The second observes the updatebutton and sets the variable to FALSE. The third observe both the inputs and the updatebutton and renders the warning message based on the variable (if it's TRUE it prints, otherwise it's empty).

The only problem I've found is that at the moment it starts off showing the warning message, but I haven't been able to figure out why.

The final code:

library(shiny)

ui <- fluidPage(titlePanel("Reactive UI"),
                sidebarLayout(
                  sidebarPanel(
                    # Reactive text should appear upon any change here
                    sliderInput(
                      "bins",
                      "Number of bins:",
                      min = 1,
                      max = 50,
                      value = 30
                    ),
                    checkboxInput(
                      inputId = "chckBxLg",
                      label = "Log scale",
                      value = FALSE
                    )
                    ,
                    actionButton("btnUpdate", "Update")
                  ),
                  mainPanel(uiOutput("msgui"),
                            plotOutput("distPlot"))
                ))

# Define server logic required to draw a histogram
server <- function(input, output) {
  # Initialize the value to check if a change happened
  Changebool <<- FALSE

  observeEvent(eventExpr = { #When an input is changed
    input$bins
    input$chckBxLg
  },
  handlerExpr = {  # Change the changebool to TRUE
    Changebool <<- TRUE
  }
  )

  observeEvent(eventExpr = { #When the update button is pressed
    input$btnUpdate
  },
  handlerExpr = {  # Change the changebool to FALSE
    Changebool <<- FALSE
  }
  )

  observeEvent({input$btnUpdate # If any of the inputs change or the update button is pressed
    input$bins
    input$chckBxLg},
               {  # Recreate the message-ui
                 output$msgui <- renderUI({
                   if (Changebool) {  # if a change has happened since last update
                     textOutput("msgTxt")  #Output text
                   } else {  #otherwise
                       #Output nothing
                   } 
                 })
               })

  # Render text
  output$msgTxt <- renderText("You have clicked something, update the chart")

  output$distPlot <- renderPlot({
    input$btnUpdate
    isolate({
      x    <- faithful[, 2]
      if (input$chckBxLg) {
        x <- log(x)
      }
      bins <-
        seq(min(x), max(x), length.out = input$bins + 1)
    })

    hist(x,
         breaks = bins,
         col = 'darkgray',
         border = 'white')
  })
}

shinyApp(ui = ui, server = server)

I must admit I was fiddling back and forth with this for quite a bit so if you notice anything weird in the code feel free to drop a comment

Marijn Stevering
  • 1,204
  • 10
  • 24
  • Hi, it looks like it could work. It looks like a similar approach to workaround for the `actionButton` reset discussed [here](https://github.com/rstudio/shiny/issues/167). – Konrad Jan 06 '17 at 16:53