shiny: start the app with hidden tabs, with NO del

2020-02-26 00:18发布

I would like to build an application and some of the tabs will be hidden to the user until he types the right password. I know how to do this with shinyjs::hideTab:

library(shiny);library(shinyjs)
ui <- fluidPage(useShinyjs(),
  navbarPage("hello", id="hello",
             tabPanel("home", br(), h3("this is home"),passwordInput("pass", "enter 'password' to see the tabs: "),actionButton("enter", "enter")),
             tabPanel("tab2", br(), h4("this is tab2")),
             tabPanel("tab3 with a lot of stuff in it", br(), h4("this is tab3"))))
server <- function(input, output, session) {
  hideTab("hello", "tab2"); hideTab("hello", "tab3 with a lot of stuff in it")
  observeEvent(input$enter, {
    if (input$pass == "password"){showTab("hello", "tab2"); showTab("hello", "tab3 with a lot of stuff in it")}})}
shinyApp(ui, server)

However there is a little "thing". In my application, the hidden tabs have a lot of stuff, like widgets, uiOutputs, plots, images, file reading in global.R, etc. The consequence is that the loading time is higher and during this loading time of the application (before the hideTab instruction gets run) the user actually sees the hidden tab and can even click on them and see what's inside. They stay "visible" for like 1 second and then get hidden.

Is there a way to make them immediately hidden, before the UI gets built? I'd prefer a solution without having to put all my ui code into the server.R script...

Thanks

4条回答
Summer. ? 凉城
2楼-- · 2020-02-26 00:59

I solved this problem creating a new function (above the UI definition) to include the style="display:none; on the right li tags:

tabsethidepanels<-function(tag, indexes = NULL) {
  if (class(tag) == "shiny.tag" && tag$name == "div" && tag$attribs$class == "tabbable") {
    if (is.null(indexes)) indexes<-seq_along(tag$children[[1]]$children[[1]])
    for (i in indexes) tag$children[[1]]$children[[1]][[i]]$attribs<-c(tag$children[[1]]$children[[1]][[i]]$attribs, list(style="display:none;"))
    tag
  } else stop("tag must be a tabsetPanel!", call. = F)
}

And define the tabset accordingly:

tabsethidepanels(
  tabsetPanel(
    id = "mytab",
    selected = "tab7-not2hide",
    tabPanel("tab1"),
    tabPanel("tab2"),
    tabPanel("tab3"),
    tabPanel("tab4"),
    tabPanel("tab5"),
    tabPanel("tab6"),
    tabPanel("tab7-not2hide")
  ),
  indexes = 1:6
)

The "indexes" field are usefull to select only the right tabs to hide:

<div class="tabbable">
  <ul class="nav nav-tabs shiny-tab-input" id="mytab" data-tabsetid="2818">
    <li style="display:none;">
      <a href="#tab-2818-1" data-toggle="tab" data-value="tab1">tab1</a>
    </li>
    <li style="display:none;">
      <a href="#tab-2818-2" data-toggle="tab" data-value="tab2">tab2</a>
    </li>
    <li style="display:none;">
      <a href="#tab-2818-3" data-toggle="tab" data-value="tab3">tab3</a>
    </li>
    <li style="display:none;">
      <a href="#tab-2818-4" data-toggle="tab" data-value="tab4">tab4</a>
    </li>
    <li style="display:none;">
      <a href="#tab-2818-5" data-toggle="tab" data-value="tab5">tab5</a>
    </li>
    <li style="display:none;">
      <a href="#tab-2818-6" data-toggle="tab" data-value="tab6">tab6</a>
    </li>
    <li class="active">
      <a href="#tab-2818-7" data-toggle="tab" data-value="tab7-not2hide">tab7-not2hide</a>
    </li>
  </ul>
  <div class="tab-content" data-tabsetid="2818">
    <div class="tab-pane" data-value="tab1" id="tab-2818-1"></div>
    <div class="tab-pane" data-value="tab2" id="tab-2818-2"></div>
    <div class="tab-pane" data-value="tab3" id="tab-2818-3"></div>
    <div class="tab-pane" data-value="tab4" id="tab-2818-4"></div>
    <div class="tab-pane" data-value="tab5" id="tab-2818-5"></div>
    <div class="tab-pane" data-value="tab6" id="tab-2818-6"></div>
    <div class="tab-pane active" data-value="tab7-not2hide" id="tab-2818-7"></div>
  </div>
</div>

Remember to select a tab not to hide as active selected = "tab7-not2hide".

If you need to hide all tabs I sugest to create a "blank" tabPanel, select his one as active and hide all tabs with indexes = NULL.

查看更多
看我几分像从前
3楼-- · 2020-02-26 01:08

You could use javascript with extendShinyjs() to hide the tabs you want on page load:

Javascript code:

shinyjs.init = function(){
  $('#hello li a[data-value="tab3_val"]').hide();
  $('#hello li a[data-value="tab2_val"]').hide();
}

R code:

ui <- fluidPage(useShinyjs(),
                #Added this js
                extendShinyjs(script = path_to_javascript_file),
                navbarPage("hello", id="hello",
                           tabPanel("home", br(), h3("this is home"),passwordInput("pass", "enter 'password' to see the tabs: "),actionButton("enter", "enter")),
                           tabPanel("tab2", value = "tab2_val", br(), h4("this is tab2")),
                           tabPanel("tab3 with a lot of stuff in it", value = "tab3_val", br(), h4("this is tab3"))))

server <- function(input, output, session) {

  observeEvent(input$enter, {
    if (input$pass == "password"){
      show(selector = '#hello li a[data-value="tab3_val"]')
      show(selector = '#hello li a[data-value="tab2_val"]')
      }})}
shinyApp(ui, server)

Alternatively the CSS actually isn't too complicated. If you wanted to go that route you could simply replace the extendShinyjs() call in the above with:

tags$head(tags$style(HTML("#hello li a[data-value = 'tab2_val'], #hello li a[data-value = 'tab3_val'] {
                             display: none;
 }")))

The downside to this is that the formatting of the tabs appears to be off after un-hiding them.

查看更多
我想做一个坏孩纸
4楼-- · 2020-02-26 01:12

How about this

library(shiny);library(shinyjs)
ui <- fluidPage(useShinyjs(),
                navbarPage("hello", id="hello",
                           tabPanel("home", br(), h3("this is home"),passwordInput("pass", "enter 'password' to see the tabs: "),actionButton("enter", "enter")),
                           tabPanel("tab2",uiOutput("tab2Content")),
                           tabPanel("tab3 with a lot of stuff in it", uiOutput("tab3Content"))))
server <- function(input, output, session) {
  output$tab2Content <- renderUI({
    req(input$pass == "password")
    tagList(
      br(), 
      h4("this is tab2")
    )
  })
  output$tab3Content <- renderUI({
    req(input$pass == "password")
    tagList(
      br(), 
      h4("this is tab3")
    )
  })}
shinyApp(ui, server)

hope this helps!

查看更多
\"骚年 ilove
5楼-- · 2020-02-26 01:25

I'd go with renderUI (see @BertilBaron's answer) or appendTab because it's easy to bypass the password when hiding the tabs:

Bypass password

Here is how to achive the desired behaviour via appendTab avoiding the above with basic shiny, no additional JS:

library(shiny)

ui <- fluidPage(navbarPage("hello", id = "hello",
  tabPanel(
    "home",
    br(),
    h3("this is home"),
    passwordInput("pass", "enter 'password' to see the tabs: "),
    actionButton("enter", "enter")
  )
))

server <- function(input, output, session) {
  observeEvent(input$enter, {
    if (input$pass == "password") {
      appendTab(inputId = "hello", tab = tabPanel("tab2", value = "tab2_val", br(), h4("this is tab2")))
      appendTab(inputId = "hello", tab = tabPanel("tab3 with a lot of stuff in it",value = "tab3_val", br(), h4("this is tab3")))
    }
  })
}

shinyApp(ui, server)
查看更多
登录 后发表回答