JasonD
JasonD

Reputation: 142

Scoping between output functions

I have been through (hopefully thoroughly enough) the shiny help article on scoping, and have also seen the answer regarding setting a global object here. Neither addresses, however, what I think (emphasis on "think") I need to do. If a variable is only created in an output function, can it be seen in another? Using action buttons, I'm trying to control lengthy calculations and some will only happen on condition of other things happening. As a minimal example,

library(shiny)
library(stringr)

fruitref <- c("apple","peach","pear","plum","banana","kiwi")
textareaInput <- function(inputID, label, value="", rows=10, columns=30) {
  HTML(paste0('<div class="form-group shiny-input-container">
              <label for="', inputID, '">', label,'</label>
              <textarea id="', inputID, '" rows="', rows,'" cols="',
              columns,'">', value, '</textarea></div>'))
}
ui <- fluidPage(titlePanel("minimal example"),
        sidebarLayout(
          sidebarPanel(
            textareaInput("inputfruit", "Enter some fruits:"),
            actionButton("checkfruit", "Check fruits"),
            verbatimTextOutput("fruitstat")
          ),
          mainPanel(
            textOutput("fruits")
          )
        )
)
server <- function(input,output){
  flag <- F
  inputs <- eventReactive(input$checkfruit,{input$inputfruit})
  continue <- reactive(flag)
  output$fruitstat <- renderText({
    inputs <- inputs()
    inputs <- str_trim(unlist(strsplit(inputs,split="\n")),side="both")
    if(length(setdiff(inputs,fruitref)) != 0 | length(inputs) == 0){
      flag <- F
      paste(paste(setdiff(inputs,fruitref), collapse=", "),"not in table.")
    } else {
      flag <- T
      "All fruits ok."
    }
  })
  output$fruits <- renderText({
    paste("Continue? ",continue())

  })
}
shinyApp(ui = ui, server = server)

There's a reference table and user input is checked against that table. User enters list in pieces until hitting the check button, then gets feedback whether all are OK, or if there's a mismatch. Only if all are okay do I want to continue. I tried setting a "continue" flag in the output function, but that didn't work, and setting it in the server outside of the output function would not update the value. I also tried creating an environment in the server function and using assign, but that too did not update the value when I tried to retrieve it for renderText later. Since it's not an input, accessible through input$, I figured I cannot use any of the update* functions.

This may not be the best way to set this kind of conditional. I saw in the tutorial the conditional pane, but that depends on an input, rather than something created in the function. More generally, there are more complicated objects to be created in the output functions (there will be four large tables and a plot at most; all or some subset will actually be output) that then other output functions should see, I did not think I could define them as reactive global objects, because until some later things happen, they can't be calculated.

Upvotes: 0

Views: 188

Answers (1)

Xiongbing Jin
Xiongbing Jin

Reputation: 12097

Define continue as an eventReactive:

continue <- eventReactive(input$checkfruit, flag)

And also use the global assignment operator inside function

        flag <<- F
        flag <<- T

Then it will work. Tested.

Upvotes: 1

Related Questions