14

I have a functional shiny app that uses the shinydashboard package.

A new feature requires user-specific behavior (e.g. use different data sets for different usernames). Therefore I intend to

  1. Display a login form
  2. Validate credentials and set a reactive value LoggedIn to true if successful
  3. Display the actual dashboardPage as soon as LoggedIn is set to TRUE

My approach is based on this app, which decides which element to display in renderUI based on the reactive value.

The following simplified examples are supposed to change the displayed UI element after clicking an actionButton. The only difference between the source is that example 1 (working as intended) uses a fixedPage, whereas example 2 (not working - clicking the button does not switch to ui2) uses a dashboardPage.

Working example

library(shiny)

ui1 <- fixedPage(actionButton("btn_login", "Login"))
ui2 <- fixedPage(sliderInput("slider", "slider", 3, 2, 2))
ui <- uiOutput("ui")

server <- function(input, output, session) {
  state <- reactiveValues(LoggedIn = FALSE)
  output$ui <- renderUI({if (!state$LoggedIn) ui1 else ui2})

  observeEvent(input$btn_login, {
    state$LoggedIn = TRUE
  })
}

shinyApp(ui, server)

Malfunctioning example

library(shiny)
library(shinydashboard)

ui1 <- fixedPage(actionButton("btn_login", "Login"))
ui2 <- dashboardPage(dashboardHeader(), dashboardSidebar(), dashboardBody())
ui <- uiOutput("ui")

server <- function(input, output, session) {
  state <- reactiveValues(LoggedIn = FALSE)
  output$ui <- renderUI({if (!state$LoggedIn) ui1 else ui2})

  observeEvent(input$btn_login, {
    state$LoggedIn = TRUE
  })
}

shinyApp(ui, server)

Is this due to peculiarities of the shinydashboard package? Has anybody had a similar problem (besides this user) and found a solution?

Thanks in advance for any help!

EDIT

@SeGa This rather useless app renders the dashboardPage after the reactiveTimer has triggered twice - Maybe there is a possibility to get it working without the timer?

library(shiny)
library(shinydashboard)

ui1 <- fixedPage(actionButton("btn_login", "Login"))
ui2 <- dashboardPage(dashboardHeader(), dashboardSidebar(), dashboardBody())
ui <- uiOutput("ui")

server <- function(input, output, session) {
  state <- reactiveValues(LoggedIn = FALSE)
  timer <- reactiveTimer(1000, session)

  output$ui <- renderUI({if (!state$LoggedIn) ui1 else ui2})

  observeEvent(timer(), {
    state$LoggedIn = !state$LoggedIn
  })
}

shinyApp(ui, server)

EDIT May 29

@Bertil Baron

Is it something like that you mean?

loginUI <- fixedPage(actionButton("btn_login", "Login"))
mainUI <- # See below
ui <- loginUI

server <- function(input, output, session) {
  observeEvent(input$btn_login, {
    removeUI(selector = "body")
    insertUI(selector = "head", where = "afterEnd", mainUI)
  })
}    
shinyApp(ui, server)

Now this works if mainUI is one of basicPage, bootstrapPage, fillPage, fixedPage, fluidPage, navbarPage - a new body tag is inserted and visible in the DOM, but there is no effect for a bootstrapPage.

In case you meant to initially display the login form in the dashboardBody and replacing it with the actual content after a successful login - that is what I wanted to avoid.

mkiesner
  • 635
  • 5
  • 20
  • 1
    Have you checked out [this](https://stackoverflow.com/a/38862184/3682794) and [this](https://github.com/treysp/shiny_password/issues/2) ? So, I dont think you can create a dashboardPage like this. – SeGa May 24 '18 at 14:24
  • Thanks for your answer. In fact I have read these. Please consider the edited post! – mkiesner May 24 '18 at 14:37
  • 2
    Hi, your problem has to do with the binding of input and outputs. Now how important is it that the complete ui is rewritten after Login you could do something much more stable with `insertUI` and `removeUI`. Would that also be a solution? – Bertil Baron May 29 '18 at 08:07
  • @BertilBaron Hey, thanks, I have updated my question! – mkiesner May 29 '18 at 09:06
  • So if I understand you correctly. You don't want the user to see the dashboard header etc.. until he is logged in? – Bertil Baron May 29 '18 at 09:13
  • Correct! The sidebar contains `selectInput` items with user-specific content that I intend to query from a database. Thus the user needs to be known - I could probably initially use an empty `selectInput ` and hide the sidebar, but I was hoping for a less ugly solution.. – mkiesner May 29 '18 at 09:30

2 Answers2

1

It also works with invalidateLater(), but also only temporary.

library(shiny)
library(shinydashboard)

ui <- uiOutput("ui")

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

  state <- reactiveValues(LoggedIn = FALSE)

  observeEvent(input$btn_login, {
    state$LoggedIn = !state$LoggedIn
  })

  ui1 <- reactive({
    fixedPage(actionButton("btn_login", "Login"))
  })

  ui2 <- reactive({
    ui2 <- dashboardPage(dashboardHeader(), dashboardSidebar(), dashboardBody(
       sliderInput("slider", "slider", min = 1, max = 10, value = 2)
     ))
    invalidateLater(100, session)
    ui2
  })

  output$ui <- renderUI({if (!state$LoggedIn) ui1() else ui2()})

}

shinyApp(ui, server)
SeGa
  • 9,454
  • 3
  • 31
  • 70
  • Hey, thanks again for your reply. Unfortunately this example constantly invalidates ui2 doesn't it? E.g. with this example a sliderInput will be reset to its initial value every 100ms. – mkiesner May 28 '18 at 09:32
  • Correct. I didnt have a slider input in the dashboard or any ui. I just saw the switch from fluidPage to dashboard. It would be interesting to know why it this makes the switch though. With `delay` from shinyjs I had no luck. – SeGa May 28 '18 at 14:53
  • This might be helpful though. This [answer](https://stackoverflow.com/questions/43244988/shiny-how-to-stop-processing-invalidatelater-after-data-was-abtained-or-at-th) gives an example of how to rewrite the invalidateLater() function. – SeGa May 28 '18 at 20:57
  • Thanks for your effort - I tried that and used `!state$LoggedIn` as a condition for the new `invalidateLater` function. The function is run once after pressing the login button (as intended), but the button remains visible and no dashboardPage is shown... :/ – mkiesner May 29 '18 at 09:09
1

Not sure this is the kind of solution you are after, but here's my attempt using shinyjs and some CSS. It seems hard to switch from a fixedPage to a dashboardPage, so if you really want to use shinydashboard, I would stick with shinydashboard and disable the dashboard look on the login page.

library(shiny)
library(shinyjs)
library(shinydashboard)

ui1 <- div(
  id = "login-page",
  actionButton("btn_login", "Login")
)

ui2 <- hidden(    
  div(
    id = "main-page",
    sliderInput("slider", "slider", 3, 2, 2)
  )
)

ui <- dashboardPage(dashboardHeader(), 
                    dashboardSidebar(collapsed = TRUE), 
                    dashboardBody(useShinyjs(),
                                  tags$head(
                                    tags$style(
                                      HTML('.main-header {
                                              display: none;
                                            }

                                            .header-visible {
                                              display: inherit;
                                            }')
                                    )
                                  ),
                                  fluidPage(ui1, ui2)
                    )
)

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

  state <- reactiveValues(LoggedIn = FALSE)

  observeEvent(input$btn_login, {
    state$LoggedIn = TRUE
    shinyjs::addClass(selector = "header", class = "header-visible")
    shinyjs::removeClass(selector = "body", class = "sidebar-collapse")
    shinyjs::hide(id = "login-page")
    shinyjs::show(id = "main-page")
  })

}

shinyApp(ui, server)

If you want to be able to come back to the login page, you can always add a login button that shows the login page, and hides the appropriate elements (sidebar/header/current page).

kluu
  • 2,848
  • 3
  • 15
  • 35
  • 1
    This seems to work in the browser (Ffox). In the RStudio window I am just seeing the slider, without the dashboard. – SeGa May 31 '18 at 16:09
  • Oh that's right, thanks for noticing and thanks for the edit. I made an edit as well. Anyway, I just read through the comments, and this looks like the 'ugly solution' @gunnar.s was talking about hehe... – kluu May 31 '18 at 16:17