2

How can I switch between page layouts in Shiny in response to user input? For example, I would like to switch between a sidebar layout to a wellpanel layout in order to have the graphics be able to span the entire page.

What I have so far seems to be on the verge of working, but I think the issue is that I can't reuse interfaces created from renderUI. Here is an example,

library(shiny)

shinyApp(
    shinyUI(
        fluidPage(
            radioButtons('layout', 'Layout:', choices=c('Sidebar', 'WellPanel'), inline=TRUE),
            conditionalPanel(
                condition = "input.layout == 'Sidebar'",
                uiOutput('sidebarUI')
            ),
            conditionalPanel(
                condition = "input.layout == 'WellPanel'",
                uiOutput('wellUI')
            )
        )
    ),
    shinyServer(function(input, output) {
        output$sidebarUI <- renderUI({
            sidebarLayout(
                sidebarPanel(
                    uiOutput('ui')
                ),
                mainPanel(
                    plotOutput('plot')
                )
            )
        })

        output$wellUI <- renderUI({
            fluidRow(
                column(12, plotOutput('plot')),
                column(12, wellPanel(uiOutput('ui')))
            )
        })

        output$ui <- renderUI({
            list(
                checkboxInput('inp1', 'Some stuff'),
                sliderInput('inp2', 'Some more stuff', 0, 10, 5)
            )
        })

        output$plot <- renderPlot({ plot(1) })
    })
)

If either one of the conditional panels is commented out, then the other one works, but only one will work with both conditional panels. How is this done?

Rorschach
  • 31,301
  • 5
  • 78
  • 129

1 Answers1

2

Hi you can't call an output twice (or more) in the ui, that's why this isn't working, instead you can do all the work in the server like this :

library(shiny)

shinyApp(
  shinyUI(
    fluidPage(
      radioButtons('layout', 'Layout:', choices=c('Sidebar', 'WellPanel'), inline=TRUE),
      uiOutput('general_ui')
    )
  ),
  shinyServer(function(input, output) {
    output$general_ui <- renderUI({
      if (input$layout == "Sidebar") {
        sidebarLayout(
          sidebarPanel(
            uiOutput('ui')
          ),
          mainPanel(
            plotOutput('plot')
          )
        )
      } else if (input$layout == "WellPanel") {
        fluidRow(
          column(12, plotOutput('plot')),
          column(12, wellPanel(uiOutput('ui')))
        )
      }
    })

    output$ui <- renderUI({
      list(
        checkboxInput('inp1', 'Some stuff'),
        sliderInput('inp2', 'Some more stuff', 0, 10, 5)
      )
    })

    output$plot <- renderPlot({ plot(1) })
  })
)

But there's a catch, when you switch between layout, the input widgets are reinitialized...

But you can fix it with something like this :

output$ui <- renderUI({
  list(
    checkboxInput('inp1', 'Some stuff'),
    if (is.null(input$inp2)) {
      sliderInput('inp2', 'Some more stuff', 0, 10, 5)
    } else {
      sliderInput('inp2', 'Some more stuff', 0, 10, input$inp2)
    }
  )
})

Before creating your widget, you'll have to check if the value already exists, in a more compact way than above you can do that :

output$ui <- renderUI({
      list(
        checkboxInput(inputId = "inp1", label = 'Some stuff', value = input$inp1 %||% FALSE),
        sliderInput(inputId = "inp2", label = 'Some more stuff', min = 0, max = 10, value = input$inp2 %||% 5)
      )
    })

With %||% function defined like this :

`%||%` <- function(a, b) {
  if (!is.null(a)) a else b
}
Victorp
  • 13,636
  • 2
  • 51
  • 55
  • I can imagine that your UI is more complicated. You can probably write a function for validate a widget. I'll look into it later. – Victorp Oct 15 '15 at 08:46