Make inputs in a sidebar persistent through tabs

若如初见. 提交于 2020-08-08 04:50:22

问题


I would like to have both a persistent sidebar (as in shinydashboard layout) and a navigation bar with tabs (as in shiny::navbarPage layout). I came across this answer that seems to correspond to what I want.

The problem is that inputs in the sidebar are not persistent through tabs, i.e when switching tabs, the inputs in the sidebar are not displayed anymore (at the contrary of shinydashboard sidebar for example). Here's an example I cannot really minimize more since a lot of it is CSS:

library(shiny)
library(bootstraplib)

# boot dash layout funs ---------------------------------------------------


boot_side_layout <- function(...) {
  div(class = "d-flex wrapper", ...)
}

boot_sidebar <- function(...) {
  div(
    class = "bg-light border-right sidebar-wrapper",
    div(class = "list-group list-group-flush", ...)
  )
}

boot_main <- function(...) {
  div(
    class = "page-content-wrapper",
    div(class = "container-fluid", ...)
  )
}

# css ---------------------------------------------------------------------

css_def <- "
body {
  overflow-x: hidden;
}

.container-fluid, .container-sm, .container-md, .container-lg, .container-xl {
    padding-left: 0px;
}

.sidebar-wrapper {
  min-height: 100vh;
  margin-left: -15rem;
  padding-left: 15px;
  padding-right: 15px;
  -webkit-transition: margin .25s ease-out;
  -moz-transition: margin .25s ease-out;
  -o-transition: margin .25s ease-out;
  transition: margin .25s ease-out;
}


.sidebar-wrapper .list-group {
  width: 15rem;
}

.page-content-wrapper {
  min-width: 100vw;
  padding: 20px;
}

.wrapper.toggled .sidebar-wrapper {
  margin-left: 0;
}

.sidebar-wrapper, .page-content-wrapper {
  padding-top: 20px;
}

.navbar{
  margin-bottom: 0px;
}

.navbar-collapse {
  font-size: 1.1rem
}

@media (max-width: 768px) {
  .sidebar-wrapper {
    padding-right: 0px;
    padding-left: 0px;

  }
}

@media (min-width: 768px) { 
  .sidebar-wrapper {
    margin-left: 0;
    position: fixed;
  }

  .page-content-wrapper {
    min-width: 0;
    width: 100%;
  }

  .wrapper.toggled .sidebar-wrapper {
    margin-left: -15rem;
  }
}

"


# app ---------------------------------------------------------------------
ui <- tagList(
  tags$head(tags$style(HTML(css_def))),
  bootstrap(),
  navbarPage(
    collapsible = TRUE,
    title = "",
    tabPanel(
      "Statistics",
      boot_side_layout(
        boot_sidebar(
          selectInput(
            "variables",
            "Variables",
            NULL
          )
        ),
        boot_main(
          fluidRow(
            dataTableOutput("statistics")
          )
        )
      )
    ),
    
    tabPanel(
      "Plots",
      boot_side_layout(
        boot_sidebar(
          
        ),
        boot_main(
        )
      )
    )
  )
)

server <- function(input, output, session) {
  
  output$statistics <- renderDataTable(mtcars[10, 10])
  
}

shinyApp(ui, server)

How can I make these inputs persistent through sidebar? (If somebody knows of another simple way to mix persistent sidebar with navbar, please show it as well).


回答1:


Why not using a sidebarLayout with a navbarPage in mainPanel?

ui <- fluidPage(
  
  sidebarLayout(
    
    sidebarPanel(
      selectInput("select", "Select", c("a", "b", "c"))
    ),
    
    mainPanel(
      navbarPage(
        "App Title",
        tabPanel("Plot"),
        tabPanel("Summary"),
        tabPanel("Table")
      )    
    )
    
  )
)

shinyApp(ui, server)

EDIT

Or something like this?

library(shiny)
library(ggplot2)

ui <- fluidPage(
  
  div(
    style = "display: flex; flex-direction: column;",
    div( #~~ Main panel ~~#
      navbarPage(
        "Old Faithful Geyser Data",
        tabPanel(
          "Plot",
          plotOutput("ggplot")
        ),
        tabPanel("Summary"),
        tabPanel("Table")
      )    
    ),
    wellPanel( #~~ Sidebar ~~#
      style = "width: 300px;",
      sliderInput("bins", "Number of bins:", min = 1, max = 50, value = 30),
    )
  )
)

server <- function(input, output) {
  output[["ggplot"]] <- renderPlot({
    x    <- faithful[, 2] 
    bins <- seq(min(x), max(x), length.out = input$bins + 1)
    hist(x, breaks = bins, col = 'darkgray', border = 'white')
  })
}

shinyApp(ui = ui, server = server)

EDIT

Like this to have the sidebar on the left:

library(shiny)
library(shinyjs)
library(ggplot2)

CSS <- "
.sidebar {
  min-width: 300px;
  margin-right: 30px;
}
#sidebar {
  width: 300px;
}
"

ui <- fluidPage(
  
  useShinyjs(),
  
  tags$head(tags$style(HTML(CSS))),
  
  div( #~~ Main panel ~~#
    navbarPage(
      "Old Faithful Geyser Data",
      tabPanel(
        "Plot",
        div(
          style = "display: flex;",
          div(class = "sidebar"),
          plotOutput("ggplot")
        )
      ),
      tabPanel(
        "Summary",
        div(
          style = "display: flex;",
          div(class = "sidebar"),
          verbatimTextOutput("summary")
        )
      ),
      tabPanel(
        "Table",
        div(
          style = "display: flex;",
          div(class = "sidebar"),
          tableOutput("table")
        )
      ),
      id = "navbar"
    )    
  ),
  wellPanel( #~~ Sidebar ~~#
    id = "sidebar", 
    sliderInput("bins", "Number of bins:", min = 1, max = 50, value = 30),
  )
)

server <- function(input, output) {
  output[["ggplot"]] <- renderPlot({
    x    <- faithful[, 2] 
    bins <- seq(min(x), max(x), length.out = input$bins + 1)
    hist(x, breaks = bins, col = 'darkgray', border = 'white')
  })
  output[["summary"]] <- renderPrint({
    list(a = 1:10, b = 1:10)
  })
  output[["table"]] <- renderTable({
    iris[1:10,]
  })
  observeEvent(input[["navbar"]], {
    selector <- 
      sprintf("$('div.tab-pane[data-value=\"%s\"] div.sidebar')", input[["navbar"]])
    runjs(paste0(selector, ".append($('#sidebar'));"))
  })
}

shinyApp(ui = ui, server = server)

EDIT

Here is an improvement of the above way. I've made some convenient functions tabPanel2 and sidebar to help the user. And I use fluidRow and column instead of using a display: flex;. This allows to have a sidebar width relative to the screen size. The example below also shows how to not include the sidebar in a tab (simply use tabPanel and not tabPanel2.

library(shiny)
library(shinyjs)
library(ggplot2)

tabPanel2 <- function(title, ..., value = title, icon = NULL, sidebarWidth = 4){
  tabPanel(
    title = title, 
    fluidRow(
      column(
        width = sidebarWidth,
        class = "sidebar"
      ),
      column(
        width = 12 - sidebarWidth,
        ...
      )
    )
  )
}

sidebar <- function(...){
  div(
    style = "display: none;",
    tags$form(
      class = "well",
      id = "sidebar",
      ...
    )
  )
}

ui <- fluidPage(
  
  useShinyjs(),
  
  div( #~~ Main panel ~~#
    navbarPage(
      "Old Faithful Geyser Data",
      tabPanel2(
        "Plot",
        plotOutput("ggplot")
      ),
      tabPanel2(
        "Summary",
        verbatimTextOutput("summary")
      ),
      tabPanel(
        "Table",
        fluidRow(
          column(
            width = 4,
            wellPanel(
              tags$fieldset(
                tags$legend(h3("About")),
                p("This app is cool")
              )
            )
          ),
          column(
            width = 8,
            tableOutput("table")
          )
        )
      ),
      id = "navbar"
    )    
  ),
  
  sidebar( #~~ Sidebar ~~#
    sliderInput("bins", "Number of bins:", min = 1, max = 50, value = 30)    
  )

)

server <- function(input, output) {
  
  output[["ggplot"]] <- renderPlot({
    x    <- faithful[, 2] 
    bins <- seq(min(x), max(x), length.out = input$bins + 1)
    hist(x, breaks = bins, col = 'darkgray', border = 'white')
  })
  
  output[["summary"]] <- renderPrint({
    list(a = 1:10, b = 1:10)
  })
  
  output[["table"]] <- renderTable({
    iris[1:10,]
  })
  
  observeEvent(input[["navbar"]], {
    selector <- 
      sprintf("$('div.tab-pane[data-value=\"%s\"] div.sidebar')", input[["navbar"]])
    append <- "selector.append($('#sidebar'));"
    js <- sprintf("var selector=%s; if(selector.length){%s;}", selector, append)
    runjs(js)
  })
  
}

shinyApp(ui = ui, server = server)


来源:https://stackoverflow.com/questions/62820406/make-inputs-in-a-sidebar-persistent-through-tabs

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!