user3434580
user3434580

Reputation: 93

How can I ensure that observe is called before renderPlot in my shiny app

I have a shiny application with two selectInputs (L1 and L2) with an observer that updates L2 based on the selection of L1 using updateSelectInput. I also have a renderPlot output that depends on both selections. The problem I face is that whenever I change L1, the renderPlot gets called twice, once with the old value of L2 and once with the new value (set in updateSelectInput). My code is below:

ui.R

shinyUI(
    fluidPage(
        titlePanel("Nested Selects Problem"),
        sidebarLayout(
            sidebarPanel(
                selectInput(
                    "L1",
                    label = "L1",
                    choices = c("red", "blue")
                    ),
                selectInput(
                    "L2",
                    label = "L2",
                    choices = ""
                    )
                ),
            mainPanel(
                plotOutput("plot")
                )
            )
        )
    )

server.R

shinyServer(
    function(input,output,session) {
        observe({
            if (input$L1 == "red") {
                choices <- c(1000000,2000000,3000000)
            }
            else {
                choices <- c(10,20,30)
            }
            updateSelectInput(session,"L2",choices=choices,selected=choices[1])
        })

        output$plot <- renderPlot({
            if (input$L2 != "") {
                plot(runif(as.numeric(input$L2)),col=input$L1)
            }
        })
    })

How can I avoid the first call to renderPlot? It seems to me that if I could arrange the observe() to be called before the first renderPlot, I would get the desired effect.

Thank you for your help.

Upvotes: 4

Views: 2001

Answers (2)

Xin Yin
Xin Yin

Reputation: 2936

Well, how about:

shinyServer(
  function(input,output,session) {
    L1_selected <- reactiveValues(triggered=-1)

    observe({
      if (input$L1 == "red") {
        choices <- c(10, 100,200,300)
      }
      else {
        choices <- c(10,20,30)
      }

      old_L2 <- isolate(input$L2)
      updateSelectInput(session,"L2",choices=choices,selected=choices[1])
      isolate(L1_selected$triggered <- L1_selected$triggered + as.numeric(old_L2 != choices[1]))
    })

    output$plot <- renderPlot({
      if (input$L2 != "") {
        if (isolate(L1_selected$triggered)) {
          isolate(L1_selected$triggered <- L1_selected$triggered - 1)
          return()
        } else {
          plot(runif(as.numeric(input$L2)),col=input$L1)
        }
      }
    })
  })

Upvotes: 1

jdharrison
jdharrison

Reputation: 30425

You can use isolate on the input$L1 call inside renderPlot. In this manner the changes should propagate from your updateSelectInput call only when input$L1 is changed :

library(shiny)
runApp(list(
  ui = fluidPage(
    titlePanel("Nested Selects Problem"),
    sidebarLayout(
      sidebarPanel(
        selectInput("L1",label = "L1",choices = c("red", "blue")),
        selectInput("L2",label = "L2",choices = "")
      ),
      mainPanel(
        plotOutput("plot")
      )
    )
  )


  , server = function(input,output,session) {
    observe({
      if (input$L1 == "red") {
        choices <- c(100,200,300)
      }
      else {
        choices <- c(10,20,30)
      }
      updateSelectInput(session,"L2",choices=choices,selected=choices[1])
    })

    output$plot <- renderPlot({
      if (input$L2 != "") {
        plot(runif(as.numeric(input$L2)),col=isolate(input$L1))
      }
    })
  })
)

Upvotes: 1

Related Questions