2
votes

I am using Shiny to build a web app. I am adding a button that will show/hide some element on the page. But after the element got hide, other page componemt does not resize themselves to fill the screen. For example, I try to hide the sidebar of a sidebarLayout, by using toggle function in shinyjs. Here's the code I have:

library(shiny)
library(shinyjs)
ui <- fluidPage(useShinyjs(), br(), wellPanel(sidebarLayout(
  sidebarPanel(id="sidebar"),
  mainPanel(wellPanel(actionButton("sideBarControl", label = "Show/Hide")))
)))
server <- function(input, output) {
  observeEvent(input$sideBarControl, {
    shinyjs::toggle(id = "sidebar")
    # potentially some statements here to fix the layout? But what should they be?
  })
}
shinyApp(ui = ui, server = server)

The sidebar hides/shows when clicking sidebarControl button correctly, but instead of resize mainPanel to fill the screen, it shifts mainPanel to the left, and left a space at the right. How to resolve this? See the pictures below:

Before hiding the sidebar

After hiding the sidebar - actual

After hiding the sidebar - expected

2
can't reproduce with shiny_1.0.3 : the panel is hidden but main panel is neither resized nor movedHubertL
I just noticed when I run from the browser it does not shift as well. I was running from R-studio view panel. But still, I want the mainPanel and contents inside to auto-resize to fill the screen width instead of just stay there.HiLos

2 Answers

4
votes

Let me respond to the comment to my previous suggestion. Since this is quite different from the previous, I write a new answer instead of editing it.

In general, you can inspect what HTML code is generated from your UI description by running the command on console. For example,

sidebarPanel(id="sidebar", actionButton("b", "btn"))
##<div class="col-sm-4">
##  <form class="well" id="sidebar">
##    <button id="b" type="button" class="btn btn-default action-button">btn</button>
##  </form>
##</div>

This tells us that sidebarPanel function generates a nested framework of

  • div with class col-sm-4; and
  • form with the supplied id

The reason why your example code did not shifts the main panel is clear now; Even if you hide sidebar, which is the form inside, there still is div that encloses it.

So we would like a way to hide the div. Unfortunately, I could not find a way to do so with sidebarPanel function. An alternative is to use column function.

column(width=3, id="col", actionButton("b", "btn"))
##<div class="col-sm-3" id="col">
##  <button id="b" type="button" class="btn btn-default action-button">btn</button>
##</div>

You can see that the output of column is kind of similar to that of sidebarPanel. Importantly, column allows you to give an ID to the div element.

So, here is a toy example that shifts the main panel to the left (i.e. fully hide the sidebar).

library(shiny)
library(shinyjs)
ui <- fluidPage(useShinyjs(), br(), wellPanel(fluidRow(
  column(width=4, id="spcol", actionButton("dummy", "dummy")),
  column(width=8, wellPanel(actionButton("sideBarControl", 
                                         label = "Show/Hide")))
)))

server <- function(input, output) {
  observeEvent(input$sideBarControl, {
    shinyjs::toggle(id = "spcol")
  })
}
shinyApp(ui = ui, server = server)

Now, let's tackle on your second point, that is, letting the main panel to fill, instead of shifting.

mainPanel()
##<div class="col-sm-8"></div>

So it does not fill because its width is set as 8. We want to change it to 12, and we can use toggleClass function from shinyjs library for that. In short, toggleClass adds a class to an element if it does not have one, and removes the class if it already does.
I believe the code below behaves as we wish.

library(shiny)
library(shinyjs)
ui <- fluidPage(useShinyjs(), br(), wellPanel(fluidRow(
  column(width=4, id="spcol", actionButton("dummy", "dummy")),
  column(width=8, id="main", wellPanel(actionButton("sideBarControl", 
                                                     label = "Show/Hide")))
)))

server <- function(input, output) {
  observeEvent(input$sideBarControl, {
    shinyjs::toggle(id = "spcol")
    shinyjs::toggleClass("main", "col-sm-8")
    shinyjs::toggleClass("main", "col-sm-12")
  })
}
shinyApp(ui = ui, server = server)

Also, here is another working example code, in which two versions of main panels with different widths are hidden and shown by turn.

library(shiny)
library(shinyjs)
ui <- fluidPage(useShinyjs(), br(), wellPanel(fluidRow(
  column(width=4, id="spcol", actionButton("dummy", "dummy")),
  column(width=8, id="main1", wellPanel(actionButton("sideBarControl", 
                                        label = "Show/Hide"))),
  column(width=12, id="main2", wellPanel(actionButton("sideBarControl2", 
                                        label = "Show/Hide")))
)))

server <- function(input, output) {
  shinyjs::toggle(id = "main2")

  observeEvent(input$sideBarControl+input$sideBarControl2, {
    shinyjs::toggle(id = "spcol")
    shinyjs::toggle(id = "main1")
    shinyjs::toggle(id = "main2")
  })
}
shinyApp(ui = ui, server = server)
0
votes

How about you try shinydashboard? It has the behavior you are looking for by default.

https://rstudio.github.io/shinydashboard/get_started.html