4

I need to block the user from accessing to other tabs until certain actions are fulfilled. In this reproducible example, I want to block the user to access the Tab 2 until he pressed the button.

This is how the app looks:

enter image description here

Here's the code for the app:

library(shiny)

ui <- shinyUI(navbarPage("",
                         tabPanel(h1("Tab1"), value = "nav1",
                                  mainPanel(
                                            br(),
                                            h2("The user must press this button to access the other tab."),
                                            br(),
                                            shiny::actionButton('button', 'press the button')
                                  )
                         ),
                         tabPanel(h1("Tab2"),
                                  value = "nav2",

                                  h3('Block access until user presses button')
                         )
)
)

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


})

# Run the application
shinyApp(ui = ui, server = server)


I would like the user to be able to see that Tab2 exists, but make it unclickable until they press the button.

Any ideas?

CodingBiology
  • 262
  • 4
  • 13
  • My first impression is that the simplest way would be to generate the contents of tab 2 using `renderUI()`. Add a dependency on `input$button` so that the UI is empty until the button is pressed, and not empty afterwards. – Limey Oct 29 '21 at 10:31

3 Answers3

2
  1. There is no need of using any server side processing. One of the modern web app development concepts is front-end and back-end separation. If you can do it on front-end, then don't use server to do the job.
  2. conditionalPanel is a better solution but users can still click the tab button, just give them an empty page.

Here is an even better solution, let's use some js to disable the tab button unless users click the action button. Users can see the tab button but it's gray and unclickable on start:

library(shiny)

ui <- shinyUI(navbarPage(
  "",
  tabPanel(
    h1("Tab1"), 
    value = "nav1",
    mainPanel(
      br(),
      h2("The user must press this button to access the other tab."),
      br(),
      shiny::actionButton('button', 'press the button', onclick = "$(tab).removeClass('disabled')")
    )
  ),
  tabPanel(
    h1("Tab2"),
    value = "nav2",
    uiOutput("tab2contents")
  ),
  tags$script(
    '
    var tab = $(\'a[data-value="nav2"]\').parent().addClass("disabled");
    $(function(){
      $(tab.parent()).on("click", "li.disabled", function(e) {
        e.preventDefault();
        return false;
      });
    });
    '
  )
))    

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

})

# Run the application
shinyApp(ui = ui, server = server)

enter image description here

lz100
  • 6,990
  • 6
  • 29
1

Use conditionalPanel(). Condition? The button shouldn't have zero clicks.

Your example now becomes:

library(shiny)

ui <- shinyUI(
  navbarPage(
    title = "", 
    
    tabPanel(
      title = h1("Tab1"), 
      value = "nav1",
      
      mainPanel(
        br(),
        h2("The user must press this button to access the other tab."),
        br(),
        shiny::actionButton('button', 'press the button')
      )
    ), 
    
    tabPanel(
      h1("Tab2"),
      value = "nav2",
      
      # ----conditional panel here----
      conditionalPanel(
        condition = "input.button != 0", 
        
        h3('Block access until user presses button')
      )
    )
  )
)

server <- shinyServer(function(input, output) {
  
  
})

# Run the application
shinyApp(ui = ui, server = server)

Mwavu
  • 1,826
  • 6
  • 14
0

Adding detail to my comment above:

library(shiny)

ui <- shinyUI(navbarPage("",
        tabPanel(
          h1("Tab1"), 
          value = "nav1",
          mainPanel(
            br(),
            h2("The user must press this button to access the other tab."),
            br(),
            shiny::actionButton('button', 'press the button')
          )
        ),
        tabPanel(
          h1("Tab2"),
          value = "nav2",
          uiOutput("tab2contents")
        )
      )
    )    

server <- shinyServer(function(input, output) {
   v <- reactiveValues(tab2Active=FALSE)
   
  observeEvent(input$button, { v$tab2Active <- TRUE})

  output$tab2contents <- renderUI({
    if (v$tab2Active) {
      h3('Tab 2 is active')
    } else {
      h3('Block access until user presses button')
    }
  })
})

# Run the application
shinyApp(ui = ui, server = server)
Limey
  • 10,234
  • 2
  • 12
  • 32
  • Thanks @Limey, yes having the tab empty is a workaround, but not entirely what I was looking for. Alternatively, another option rather than blocking the second tab, would be to make the second tab appear after the button is pressed. But I haven't achieved that either. Thanks, anyway! – CodingBiology Oct 29 '21 at 11:32
  • You can use the same technique to make the second tab "appear" after the button press: just generate the entire `tabPanel` in the `renderUI` call. That would have been my preferred option, as it happens, but I took your "block access" to mean you wanted the tab to be there but inaccessible. A third option would be to hide or deactivate all the widgets on the second tab to start with. But that would require toggling each widget individually, and so is more work than either of the other two approaches. – Limey Oct 29 '21 at 11:50
  • Cool! I'm trying to fiddling now with CSS. Still trying to achieve the blocked part. https://stackoverflow.com/questions/40147856/disable-click-event-on-all-controls-using-javascript – CodingBiology Oct 29 '21 at 11:59