简体   繁体   English

输入密码后启动 Shiny 应用程序(使用 Shinydashboard)

[英]Starting Shiny app after password input (with Shinydashboard)

In this topic is well explained how to start the shinyapp after some password input.在本主题中很好地解释了如何在输入一些密码后启动 Shinyapp。 I am trying to do the same, but instead of "navbarPage", I would like to have a "dashboardPage".我正在尝试做同样的事情,但我想要一个“dashboardPage”而不是“navbarPage”。

I tried to change the argument in do.call function form 'navbarPage' to 'dashboardPage', but the app crashes.我试图将 do.call 函数表单“navbarPage”中的参数更改为“dashboardPage”,但应用程序崩溃了。

rm(list = ls())
library(shiny)

Logged = FALSE;
my_username <- "test"
my_password <- "test"

ui1 <- function(){
  tagList(
    div(id = "login",
        wellPanel(textInput("userName", "Username"),
                  passwordInput("passwd", "Password"),
                  br(),actionButton("Login", "Log in"))),
    tags$style(type="text/css", "#login {font-size:10px;   text-align: left;position:absolute;top: 40%;left: 50%;margin-top: -100px;margin-left: -150px;}")
  )}

ui2 <- function(){tagList(tabPanel("Test"))}

ui = (htmlOutput("page"))
server = (function(input, output,session) {

  USER <- reactiveValues(Logged = Logged)

  observe({ 
    if (USER$Logged == FALSE) {
      if (!is.null(input$Login)) {
        if (input$Login > 0) {
          Username <- isolate(input$userName)
          Password <- isolate(input$passwd)
          Id.username <- which(my_username == Username)
          Id.password <- which(my_password == Password)
          if (length(Id.username) > 0 & length(Id.password) > 0) {
            if (Id.username == Id.password) {
              USER$Logged <- TRUE
            } 
          }
        } 
      }
    }    
  })
  observe({
    if (USER$Logged == FALSE) {

      output$page <- renderUI({
        div(class="outer",do.call(bootstrapPage,c("",ui1())))
      })
    }
    if (USER$Logged == TRUE) 
    {
      output$page <- renderUI({
        div(class="outer",do.call(dashboardPage,c(inverse=TRUE,title = "Contratulations you got in!",ui2())))
      })
      print(ui)
    }
  })
})

runApp(list(ui = ui, server = server))

I woder if my code is enough to get you started on the "right" path.我想知道我的代码是否足以让您开始“正确”的道路。 Please let me know if it is not the case.如果不是这样,请告诉我。

The code below, if the login and password are correct, will display a shinydashboard.下面的代码,如果登录名和密码正确,将显示一个闪亮的仪表板。

but the following issues will need addressing:但需要解决以下问题:

  • There is a problem in the css. css有问题。 I think you need to "reset" the css changed for the login operation to something more standard to shinydashboard (currently it is all white)我认为您需要“重置”为登录操作更改的 css 为更标准的shinydashboard(目前它都是白色的)
  • If the password is wrong, the first observe will keep on "winning" on the renderUI (with or without a second observe , strictly speaking unnecessary hence eliminated) and the message relative to the wrong login is never executed.如果密码错误,第一个observe将继续在renderUI 上“获胜”(有或没有第二个observe ,严格来说是不必要的,因此被消除)并且永远不会执行与错误登录相关的消息。

There are number of things you could try to fix the above.您可以尝试解决许多问题。

  • For the css you could either re-set it, or elegantly have the login in a modal.对于 css,您可以重新设置它,或者优雅地在模态中登录。
  • For the second perhaps you could bring all the logic into the renderUI call.第二,也许您可​​以将所有逻辑带入 renderUI 调用中。 This would make sure that all cases are executed.这将确保所有案例都被执行。

But please let me know if it is clear enough.但请让我知道它是否足够清楚。

This is the code:这是代码:

rm(list = ls())
library(shiny)
library(shinydashboard)

Logged = FALSE

my_username <- "test"
my_password <- "test"

ui1 <- function() {
  tagList(
    div(
      id = "login",
      wellPanel(
        textInput("userName", "Username"),
        passwordInput("passwd", "Password"),
        br(),
        actionButton("Login", "Log in")
      )
    ),
    tags$style(
      type = "text/css",
      "#login {font-size:10px;   text-align: left;position:absolute;top: 40%;left: 50%;margin-top: -100px;margin-left: -150px;}"
    )
  )
}

ui2 <- function() {
  tagList(dashboardHeader(),
          dashboardSidebar(),
          dashboardBody("Test"))
}


ui = (htmlOutput("page"))

server = function(input, output, session) {
  USER <- reactiveValues(Logged = Logged)

  observe({
    if (USER$Logged == FALSE) {
      if (!is.null(input$Login)) {
        if (length(input$Login) > 0) {
          Username <- isolate(input$userName)
          Password <- isolate(input$passwd)
          Id.username <- which(my_username == Username)
          Id.password <- which(my_password == Password)
          if (length(Id.username) > 0 &
              length(Id.password) > 0) {
            if (Id.username == Id.password) {
              USER$Logged <- TRUE
            }
          }
        }
      }
    }
  })

  output$page <- renderUI({
    if (USER$Logged == FALSE) {
      do.call(bootstrapPage, c("", ui1()))
    } else {
      do.call(dashboardPage, #c(inverse=TRUE,title = "Contratulations you got in!",
              ui2())
    }
  })
}

shinyApp(ui, server)

October 30, 2017 Update 2017 年 10 月 30 日更新

It seems that the above code doesn't work anymore (thanks to @5249203 for pointing this out).上面的代码似乎不再起作用(感谢@5249203 指出这一点)。

I've tried to fix it, but I haven't managed to make the do.call function work with dashboardBody (if somebody knows of a way, please let me know!).我试图修复它,但我还没有设法使do.call函数与dashboardBody工作(如果有人知道某种方法,请告诉我!)。

Therefore I approached the problem in another way, thanks to recent shiny functions.因此,由于最近的shiny功能,我以另一种方式解决了这个问题。

See what you think (of course as usual the solution is just a template needing extensions).看看你的想法(当然,像往常一样,解决方案只是一个需要扩展的模板)。

library(shiny)
library(shinydashboard)

Logged = FALSE
my_username <- "test"
my_password <- "test"

ui <- dashboardPage(skin='blue',
  dashboardHeader( title = "Dashboard"),
  dashboardSidebar(),
  dashboardBody("Test",
    # actionButton("show", "Login"),
  verbatimTextOutput("dataInfo")
    )
)

server = function(input, output,session) {

values <- reactiveValues(authenticated = FALSE)

# Return the UI for a modal dialog with data selection input. If 'failed' 
# is TRUE, then display a message that the previous value was invalid.
dataModal <- function(failed = FALSE) {
  modalDialog(
    textInput("username", "Username:"),
    passwordInput("password", "Password:"),
    footer = tagList(
      # modalButton("Cancel"),
      actionButton("ok", "OK")
    )
  )
}

# Show modal when button is clicked.  
# This `observe` is suspended only whith right user credential

obs1 <- observe({
  showModal(dataModal())
})

# When OK button is pressed, attempt to authenticate. If successful,
# remove the modal. 

obs2 <- observe({
  req(input$ok)
  isolate({
    Username <- input$username
    Password <- input$password
  })
  Id.username <- which(my_username == Username)
  Id.password <- which(my_password == Password)
  if (length(Id.username) > 0 & length(Id.password) > 0) {
    if (Id.username == Id.password) {
      Logged <<- TRUE
        values$authenticated <- TRUE
        obs1$suspend()
        removeModal()

    } else {
      values$authenticated <- FALSE
    }     
  }
  })


output$dataInfo <- renderPrint({
  if (values$authenticated) "OK!!!!!"
  else "You are NOT authenticated"
})

}

shinyApp(ui,server)

Here is another solution that takes a slightly different approach than @Enzo's.这是另一种与@Enzo 的方法略有不同的解决方案。 It creates a second UI so users cannot see what the app is displaying on the first menu tab.它创建了第二个 UI,因此用户无法看到应用程序在第一个菜单选项卡上显示的内容。 The only downside is everything is basically brought to the Server side which may cause some issues for your code depending on how it is written.唯一的缺点是一切基本上都被带到了服务器端,这可能会导致您的代码出现一些问题,具体取决于它的编写方式。

library(shiny)
library(shinydashboard)

my_username <- "test"
my_password <- "abc"

###########################/ui.R/##################################

header <- dashboardHeader(title = "my heading")
sidebar <- dashboardSidebar(uiOutput("sidebarpanel"))
body <- dashboardBody(uiOutput("body") )

ui <- dashboardPage(header, sidebar, body)

###########################/server.R/##################################

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

  USER <<- reactiveValues(Logged = Logged)

  observe({ 
    if (USER$Logged == FALSE) {
      if (!is.null(input$Login)) {
        if (input$Login > 0) {
          Username <- isolate(input$userName)
          Password <- isolate(input$passwd)
          Id.username <- which(my_username == Username)
          Id.password <- which(my_password == Password)
          if (length(Id.username) > 0 & length(Id.password) > 0) {
            if (Id.username == Id.password) {
              USER$Logged <<- TRUE
            } 
          }
        } 
      }
    }    
  })

  output$sidebarpanel <- renderUI({
    if (USER$Logged == TRUE) { 
      dashboardSidebar(
        sidebarUserPanel("myuser", subtitle = a(icon("user"), "Logout", href="")),
        selectInput("in_var", "myvar", multiple = FALSE,
                    choices = c("option 1","option 2")),
        sidebarMenu(
          menuItem("Item 1", tabName = "t_item1", icon = icon("line-chart")),
          menuItem("Item 2", tabName = "t_item2", icon = icon("dollar")),
          menuItem("Item 3", tabName = "t_item3", icon = icon("credit-card")),
          menuItem("Item 4", tabName = "t_item4", icon = icon("share-alt"))
        ))}
  })

  output$body <- renderUI({
    if (USER$Logged == TRUE) {
      B <- c(2,3,4,3,7,5,4)

      box(
        title = p("Histogram", actionLink("Expand", "", icon = icon("expand"))), status = "primary", solidHeader = TRUE, width = 4,
        hist(B)
      )
    }
    if (USER$Logged == FALSE) {
      box(title = "Login",textInput("userName", "Username"),
          passwordInput("passwd", "Password"),
          br(),
          actionButton("Login", "Log in"))
    }
  })
}

shinyApp(ui, server)

September 2018 Update 2018 年 9 月更新

I was able to figure out @Enzo's original code to make the do.call function work with shinydashboard .我能够找出shinydashboard的原始代码,使do.call函数与shinydashboard工作。 Please see below.请参阅下文。 Credit to @Enzo for this, I just slightly changed some lines.归功于@Enzo,我只是稍微更改了一些行。 I think this solution is better than my first code above since it allows the correct output codes to stay in the UI side.我认为这个解决方案比我上面的第一个代码更好,因为它允许正确的输出代码留在 UI 端。 I've also added a message pop-up if the username and password is incorrect.如果用户名和密码不正确,我还添加了一个消息弹出窗口。

rm(list = ls())
library(shiny)
library(shinydashboard)

my_username <- "test"
my_password <- "abc"

###########################/ui.R/##################################

ui1 <- function(){
  tagList(
    div(id = "login",
        wellPanel(textInput("userName", "Username"),
                  passwordInput("passwd", "Password"),
                  br(),
                  actionButton("Login", "Log in"),
                  verbatimTextOutput("dataInfo")
        )
    ),
    tags$style(type="text/css", "#login {font-size:10px;   text-align: left;position:absolute;top: 40%;left: 50%;margin-top: -100px;margin-left: -150px;}")
  )}

ui2 <- function(){tagList(
  "You did it!"
)}

header <- dashboardHeader(title = "Test Login")
sidebar <- dashboardSidebar()
body <- dashboardBody(
  tags$head(tags$style("#dataInfo{color: red")),
  htmlOutput("page")
)

ui <- dashboardPage(header, sidebar, body)

###########################/server.R/##################################

server = (function(input, output,session) {

  Logged <- FALSE
  Security <- TRUE

  USER <- reactiveValues(Logged = Logged)
  SEC <- reactiveValues(Security = Security)

  observe({ 
    if (USER$Logged == FALSE) {
      if (!is.null(input$Login)) {
        if (input$Login > 0) {
          Username <- isolate(input$userName)
          Password <- isolate(input$passwd)
          if(my_username == Username & my_password == Password) {
            USER$Logged <- TRUE
          } else {SEC$Security <- FALSE}
        } 
      }
    }    
  })

  observe({
    if (USER$Logged == FALSE) {output$page <- renderUI({ui1()})}
    if (USER$Logged == TRUE) {output$page <- renderUI({ui2()})}
  })

  observe({
    output$dataInfo <- renderText({
      if (SEC$Security) {""}
      else {"Your username or password is not correct"}
    })
  })

})

runApp(list(ui = ui, server = server))

Your example uses a single user.您的示例使用单个用户。 I made some modifications for multiple user/password situations.我对多个用户/密码情况做了一些修改。 This seems to work for me.这似乎对我有用。 Hopefully, others may find it helpful:希望其他人可能会发现它有帮助:

library(shiny)
library(shinydashboard)
library(tidyverse)

user_base <- tibble(
  user =     c("Test1", "Test2", "Test3"),
  password = c("abc", "bcd", "cde"),
  name =     c("User1", "User2", "User3")
)

###########################/ui.R/##################################

header <- dashboardHeader(title = "my heading")
sidebar <- dashboardSidebar(uiOutput("sidebarpanel"))
body <- dashboardBody(uiOutput("body") )
ui <- dashboardPage(header, sidebar, body)

###########################/server.R/##################################

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

  USER <<- reactiveValues(Logged = Logged)

  observe({ 
    if (USER$Logged == FALSE) {
      if (!is.null(input$Login)) {
        if (input$Login > 0) {
          Username <- isolate(input$userName)
          Password <- isolate(input$passwd)
          Id.username <- which(user_base$user == Username)
          Id.password <- which(user_base$password == Password)
          if (length(Id.username) > 0 & length(Id.password) > 0) {
            if (Id.username == Id.password) {
              USER$Logged <<- TRUE
            } 
          }
        } 
      }
    }    
  })

  output$sidebarpanel <- renderUI({
    if (USER$Logged == TRUE) { 
      dashboardSidebar(
        sidebarUserPanel("myuser", subtitle = a(icon("user"), "Logout", href="")),
        selectInput("in_var", "myvar", multiple = FALSE,
                    choices = c("option 1","option 2")),
        sidebarMenu(
          menuItem("Item 1", tabName = "t_item1", icon = icon("line-chart")),
          menuItem("Item 2", tabName = "t_item2", icon = icon("dollar")),
          menuItem("Item 3", tabName = "t_item3", icon = icon("credit-card")),
          menuItem("Item 4", tabName = "t_item4", icon = icon("share-alt"))
        ))}
  })

  output$body <- renderUI({
    if (USER$Logged == TRUE) {
      B <- c(2,3,4,3,7,5,4)

      box(
        title = p("Histogram", actionLink("Expand", "", icon = icon("expand"))), 
        status = "primary", solidHeader = TRUE, width = 4,
        hist(B)
      )
    }
    if (USER$Logged == FALSE) {
      box(title = "Login",textInput("userName", "Username"),
          passwordInput("passwd", "Password"),
          br(),
          actionButton("Login", "Log in"))
    }
  })
}

shinyApp(ui, server)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何将verbatimTextOutput的字体系列更改为与Shiny和Shinydashboard中的输入相同? - How to change the font family of verbatimTextOutput to be the same as the input in Shiny and Shinydashboard? shiny 选项卡项目闪亮仪表板 - shiny tab Items shinydashboard 使用shinydashboard的infoBox变成闪亮的 - Use infoBox from shinydashboard into shiny Shiny + CSS:在shinydashboard侧边栏中对齐actionButtons - Shiny + CSS: Aligning actionButtons in shinydashboard sidebar 如何在 Shiny 和 Shinydashboard 中更改 verbatimTextOutput 的宽度和高度 - How to change the width and height of verbatimTextOutput in Shiny and Shinydashboard 将 shiny 小部件 header 与 shiny 应用程序中的输入放在同一行 - Place shiny widget header in the same line with the input in shiny app 输入密码和:伪元素后的文本 - input password and :after pseudoelement text 按钮对 Shiny 应用程序中的 slideBar 输入的更改没有反应 - Button is not reactive to changes to slideBar input in Shiny app 如何在 Shiny 和 Shinydashboard 中自定义 HTML 文本和逐字文本输出之间的空间 - How to customize the space between an HTML text and verbatimTextOutput in Shiny and Shinydashboard 如何告诉<input type="password"> ConfirmationPassword 可以是:valid AFTER after it is = to<input> 密码 - How to tell to <input type=password>ConfirmationPassword</input> when it can be :valid AFTER it is = to <input>Password
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM