Tyler Rinker
Tyler Rinker

Reputation: 109874

Shiny initial textAreaInput value and reactive after each button press

Description

I have a textAreaInput box that I want to start with a default value. The user can click 2 actionButtons (Submit & Random Comment). Submit updates the comment from the textAreaInput for further processing (plot, etc.) while Random Comment sends a new random value to textAreaInput (the user may type in the textAreaInput box as well). I almost have it but can't get the app to update textAreaInput's value until the Submit button is pressed.

Question

I want it to be updated when Random Comment is pressed but still allow the user to erase the text box and type their own text. How can I make the app do this?

enter image description here

MWE

library(shiny)
library(shinyjs)
library(stringi)

shinyApp(
    ui = fluidPage(
      column(2,
        uiOutput("randcomment"),
        br(),
        div(
            actionButton("randtext", "Random Comment", icon = icon("quote-right")),
            div(actionButton("submit", "Submit", icon = icon("refresh")), style="float:right")
        )

      ),
      column(4, div(verbatimTextOutput("commenttext"), style = 'margin-top: 2cm;'))
    ),
    server = function(input, output) {

        output$randcomment <- renderUI({
            commentUi()
        })

        comment_value <- reactiveValues(default = 0)

        observeEvent(input$submit,{
            comment_value$default <- input$randtext
        })

        renderText(input$randtext)

        commentUi <- reactive({

            if (comment_value$default == 0) {
                com <- stri_rand_lipsum(1)
            } else {
                com <- stri_rand_lipsum(1)
            }

            textAreaInput("comment", label = h3("Enter Course Comment"), 
                value = com, height = '300px', width = '300px')
        })

        output$commenttext <- renderText({ input$comment })


    }
  )

Upvotes: 4

Views: 1429

Answers (1)

Benjamin
Benjamin

Reputation: 17279

I'd approach this a little bit differently. I would use reactiveValues to populate both of the fields, and then use two observeEvents to control the contents of the reactiveValues.

I don't think you need a reactive at all in this situation. reactive is good when you want immediate processing. If you want to maintain control over when the value is processed, use reactiveValues.

library(shiny)
library(shinyjs)
library(stringi)

shinyApp( 
  ui = fluidPage(
    column(2,
           uiOutput("randcomment"),
           br(),
           div(
             actionButton("randtext", "Random Comment", icon = icon("quote-right")),
             div(actionButton("submit", "Submit", icon = icon("refresh")), style="float:right")
           )

    ),
    column(4, div(verbatimTextOutput("commenttext"), style = 'margin-top: 2cm;'))
  ),
  server = function(input, output) {

    # Reactive lists -------------------------------------------------------
    # setting the initial value of each to the same value.
    initial_string <- stri_rand_lipsum(1)
    comment_value <- reactiveValues(comment = initial_string,
                                    submit = initial_string)

    # Event observers ----------------------------------------------------
    observeEvent(input$randtext,
      {
        comment_value$comment <- stri_rand_lipsum(1)
      }
    )

    # This prevents the comment_value$submit from changing until the 
    # Submit button is clicked. It changes to the value of the input
    # box, which is updated to a random value when the Random Comment
    # button is clicked.
    observeEvent(input$submit,
      {
        comment_value$submit <- input$comment
      }
    )

    # Output Components -------------------------------------------------
    # Generate the textAreaInput
    output$randcomment <- renderUI({
      textAreaInput("comment", 
                    label = h3("Enter Course Comment"), 
                    value = comment_value$comment, 
                    height = '300px', 
                    width = '300px')
    })

    # Generate the submitted text display
    output$commenttext <- 
      renderText({ 
        comment_value$submit 
      })
  }
)

Some comments on your code

I struggled a little with determining what your code was doing. Part of the reason was that your server function was organized a bit chaotically. Your components are

  • output
  • reactive list
  • observer
  • output (but not assigned to a slot...superfluous)
  • reactive object
  • output

I'd recommend grouping your reactives together, your observers together, and your outputs together. If you have truly separate systems, you can break the systems into different sections of code, but have them follow a similar pattern (I would claim that these two boxes are part of the same system)

Your commentUi reactive has a strange if-else construction. It always sets com to a random string. What's more, the if-else construction isn't really necessary because no where in your code do you ever update comment_value$default--it is always 0. It looks like you may have been trying to base this off of an action button at some point, and then concluded (rightly) that that wasn't a great option.

Also, I would advise against building UI components in your reactive objects. You'll find your reactives are much more flexible and useful if they return values and then build any UI components within the render family of functions.

Upvotes: 7

Related Questions