【问题标题】:In R Shiny, possible to use multiple conditions in conditional panel?在 R Shiny 中,可以在条件面板中使用多个条件吗?
【发布时间】:2021-11-05 08:12:34
【问题描述】:

在下面的 MWE 代码中,运行代码生成的两个选项卡“负债模块”和“利率”是相同的。它们旨在作为当前通过运行代码生成的同一数据表和绘图(显示速率)的两条不同路径。

但是这两个选项卡需要随着它们的进一步发展而分道扬镳,就侧边栏面板中的其他操作按钮而言,以及出现在每个相应选项卡的主面板顶部的操作按钮而言。举个简单的例子,我想在“负债模块”而不是“利率”模块中添加一个“测试”操作按钮。

如何将多个条件添加到条件面板,因此在这种情况下,“测试”操作按钮会出现在“负债模块”中,但不会出现在“利率”标签中?如下图所示。

我的谦虚尝试在下面的 MWE 中标记为 # ATTEMPT #;当然,它不起作用,所以我不得不将其注释掉。

MWE 代码:

library(shiny)
library(shinyMatrix)
library(shinyjs)

matrix4Default <- matrix(c(0.2), 4, 1, dimnames = list(c("A", "B", "C", "D"), NULL))

matrix4Input <- function(x, matrix4Input) {
  matrixInput(
    x,
    value = matrix4Input,
    rows = list(extend = FALSE, names = TRUE),
    cols = list(
      extend = FALSE,
      names = FALSE,
      editableNames = FALSE
    ),
    class = "numeric"
  )
}

vectorBaseRate <- function(x, y) {
  a <- rep(y, x)
  b <- seq(1:x)
  c <- data.frame(x = b, y = a)
  return(c)
}

vectorBaseRatePlot <- function(w, x, y, z) {
  plot(
    w[, 1],
    sapply(w[, 2], function(x)
      gsub("%", "", x)),
    main = x,
    xlab = y,
    ylab = z
  )
}

ui <- pageWithSidebar(
  headerPanel("Model..."),
  sidebarPanel(fluidRow(helpText(h5(
    strong("Base Input Panel")
  ))), uiOutput("Panels")),
  mainPanel(tabsetPanel(
    tabPanel(
      "Liabilities module",
      value = 4,
      fluidRow(
        radioButtons(
          inputId = "showRates4",
          label = h5(strong(helpText(
            "Select model output to view:"
          ))),
          choices = c('Rates values', 'Rates plots'),
          selected = 'Rates values',
          inline = TRUE
        ),
        uiOutput('showTab4Results')
      )
    ),
    # close tab panel
    tabPanel(
      "Interest rates",
      value = 5,
      fluidRow(
        radioButtons(
          inputId = "showRates5",
          label = h5(strong(helpText(
            "Select model output to view:"
          ))),
          choices = c('Rates values', 'Rates plots'),
          selected = 'Rates values',
          inline = TRUE
        ),
        uiOutput('showTab5Results')
      )
    ),
    # close tab panel
    id = "tabselected"
  ))
) # close tabset panel, main panel, page with sidebar

server <- function(input, output, session) {
  matrix4   <- reactive(input$matrix4)
  baseRate  <-
    function() {
      vectorBaseRate(60, input$matrix4[1, 1])
    } # Must remain in server section
  
  output$Panels <- renderUI({
    conditionalPanel(
      condition = "input.tabselected==4 || input.tabselected==5", actionButton('modRates', 'Modify Rates'),
      # ATTEMPT # condition = "input.tabselected==4", actionButton('test','Test')
    ) # close conditional panel
  }) # close renderUI
  
  vectorRates <- reactive({
    if (is.null(input$modRates)){DF <- NULL}
    else {
      if (input$modRates < 1) {DF <- cbind(Period = 1:60, BaseRate = 0.2)}
      else {
        req(input$matrix4)
        DF <- cbind(Period = 1:60, BaseRate = baseRate()[, 2])
      } # close 2nd else
    } # close 1st else
    DF
  }) # close reactive
  
  observeEvent(input$resetRatesStruct, {updateMatrixInput(session, 'matrix4', matrix4Default)})
  
  output$table5 <- output$table4 <- renderTable({vectorRates()})
  
  output$graph5 <- output$graph4 <- renderPlot({
    vectorBaseRatePlot(vectorRates(), "A Variable", "Period", "Rate")
  })
  
  output$showTab4Results <- renderUI({
    if (input$showRates4 == 'Rates values'){tableOutput("table4")} 
    else {plotOutput("graph4")}
  })
  
  output$showTab5Results <- renderUI({
    if (input$showRates5 == 'Rates values'){tableOutput("table5")} 
    else {plotOutput("graph5")}
  })
  
  observeEvent(input$modRates,
               {
                 showModal(modalDialog(
                   matrix4Input("matrix4", if (is.null(input$matrix4))
                     matrix4Default
                     else
                       input$matrix4),
                   useShinyjs(),
                   footer = tagList(
                     actionButton("resetRatesStruct", "Reset"),
                     modalButton("Close")
                   )
                 ))
               } # close modalDialog, showModal, and showModal function
  ) # close observeEvent
} # close server

shinyApp(ui, server)

【问题讨论】:

    标签: r shiny shiny-reactivity


    【解决方案1】:

    您可以使用不同的条件简单地添加另一个conditionalPanel

    此外,我删除了所有renderUI,因为没有必要在服务器端创建条件面板。这应该会带来更快的 UI。

    我添加了更多按钮来展示这个概念:

    library(shiny)
    library(shinyMatrix)
    library(shinyjs)
    
    matrix4Default <- matrix(c(0.2), 4, 1, dimnames = list(c("A", "B", "C", "D"), NULL))
    
    matrix4Input <- function(x, matrix4Input) {
      matrixInput(
        x,
        value = matrix4Input,
        rows = list(extend = FALSE, names = TRUE),
        cols = list(
          extend = FALSE,
          names = FALSE,
          editableNames = FALSE
        ),
        class = "numeric"
      )
    }
    
    vectorBaseRate <- function(x, y) {
      a <- rep(y, x)
      b <- seq(1:x)
      c <- data.frame(x = b, y = a)
      return(c)
    }
    
    vectorBaseRatePlot <- function(w, x, y, z) {
      plot(
        w[, 1],
        sapply(w[, 2], function(x)
          gsub("%", "", x)),
        main = x,
        xlab = y,
        ylab = z
      )
    }
    
    ui <- pageWithSidebar(
      headerPanel("Model..."),
      sidebarPanel(fluidRow(helpText(h5(
        strong("Base Input Panel")
      ))),
      conditionalPanel(
        condition = "input.tabselected==4 || input.tabselected==5", actionButton('modRates', 'Modify Rates')
      ), # close conditional panel
      conditionalPanel(
        condition = "input.tabselected==4", actionButton('test1','Test')
      )
      ),
      mainPanel(
        conditionalPanel(
          condition = "input.tabselected==4", actionButton('test2','A mainPanel test button')
        ),
        conditionalPanel(
          condition = "input.tabselected==5", actionButton('test3','Another mainPanel test button')
        ),
        tabsetPanel(
          selected = 4,
          conditionalPanel(
            condition = "input.tabselected==4", actionButton('test4','A tabsetPanel test button')
          ),
          conditionalPanel(
            condition = "input.tabselected==5", actionButton('test5','Another tabsetPanel test button')
          ),
          tabPanel(
            "Liabilities module",
            value = 4,
            fluidRow(
              radioButtons(
                inputId = "showRates4",
                label = h5(strong(helpText(
                  "Select model output to view:"
                ))),
                choices = c('Rates values', 'Rates plots'),
                selected = 'Rates values',
                inline = TRUE
              ),
              conditionalPanel(condition = "input.showRates4 == 'Rates values'", tableOutput("table4")),
              conditionalPanel(condition = "input.showRates4 == 'Rates plots'", plotOutput("graph4"))
            )
          ),
          # close tab panel
          tabPanel(
            "Interest rates",
            value = 5,
            fluidRow(
              radioButtons(
                inputId = "showRates5",
                label = h5(strong(helpText(
                  "Select model output to view:"
                ))),
                choices = c('Rates values', 'Rates plots'),
                selected = 'Rates values',
                inline = TRUE
              ),
              conditionalPanel(condition = "input.showRates5 == 'Rates values'", tableOutput("table5")),
              conditionalPanel(condition = "input.showRates5 == 'Rates plots'", plotOutput("graph5"))
            )
          ),
          # close tab panel
          id = "tabselected"
        ))
    ) # close tabset panel, main panel, page with sidebar
    
    server <- function(input, output, session) {
      matrix4   <- reactive(input$matrix4)
      baseRate  <- function() {
        vectorBaseRate(60, input$matrix4[1, 1])
      } # Must remain in server section
      
      vectorRates <- reactive({
        if (is.null(input$modRates)){DF <- NULL}
        else {
          if (input$modRates < 1) {DF <- cbind(Period = 1:60, BaseRate = 0.2)}
          else {
            req(input$matrix4)
            DF <- cbind(Period = 1:60, BaseRate = baseRate()[, 2])
          } # close 2nd else
        } # close 1st else
        DF
      }) # close reactive
      
      observeEvent(input$resetRatesStruct, {updateMatrixInput(session, 'matrix4', matrix4Default)})
      
      output$table5 <- output$table4 <- renderTable({vectorRates()})
      
      output$graph5 <- output$graph4 <- renderPlot({
        vectorBaseRatePlot(vectorRates(), "A Variable", "Period", "Rate")
      })
      
      observeEvent(input$modRates,
                   {
                     showModal(modalDialog(
                       matrix4Input("matrix4", if (is.null(input$matrix4))
                         matrix4Default
                         else
                           input$matrix4),
                       useShinyjs(),
                       footer = tagList(
                         actionButton("resetRatesStruct", "Reset"),
                         modalButton("Close")
                       )
                     ))
                   } # close modalDialog, showModal, and showModal function
      ) # close observeEvent
    } # close server
    
    shinyApp(ui, server)
    

    【讨论】:

    • 谢谢您,您还展示了如何在不同的可能位置添加其他按钮,这正是我要解决的问题。您还展示了如何为相同的“条件=”设置多个条件面板。在 UI 部分工作。为了在 renderUI 中做到这一点,我发布了另一个答案(我更喜欢它),您可以在其中嵌套条件面板。我使用 renderUI 在服务器部分渲染面板的原因是在第一次调用应用程序时停止这种奇怪的闪现。但我会尝试您将条件面板移回 UI 并查看是否仍有调用闪烁的建议。
    • 根据我使用 consitionalPanel 的经验,它是引入条件 UI 元素的响应速度最快的解决方案。使用renderUI,您需要等待服务器和所有必要的通信完成。到目前为止,我还没有在我制作的应用程序中看到任何闪烁。
    • 我正在按照您的建议转换我的非 MWE 代码,消除 renderUI 并将所有条件面板移动到 UI 部分。它肯定会更好地流动,并且在直觉上更有意义。我将检查非 MWE 代码中的调用闪烁(我已经看到了),并将发布该代码以查看是否有另一种方法来解决 Shiny 调用闪烁。 RenderUI 确实施加了一些限制,很高兴摆脱它!
    • 听起来不错 - 如果您有显示闪烁问题的 MWE,请告诉我。干杯
    • 我已经完成了将所有条件面板从服务器移动到 UI 部分,消除了从这个 MWE 提取的完整模块中的 renderUI 及其复杂性。没有调用闪烁!至少对于这个“负债”模块。我将尝试对另一个相关模块进行相同的修改,希望在这种情况下不会闪烁调用。这种 UI 方法当然更容易理解。
    【解决方案2】:

    您可以使用 if 语句,因为您在服务器中呈现 UI。或者,您可以在服务器中创建整个侧边栏,为您提供更多的灵活性。您现在实际上可以删除整个条件面板,只需使用 input$tabselected 值作为条件生成要在 UI 中使用的按钮,这取决于您。

    output$Panels <- renderUI({
        conditionalPanel(
          condition = "input.tabselected==4 || input.tabselected==5", 
          actionButton('modRates', 'Modify Rates'),
          if(input$tabselected == 4){
            actionButton('test','Test')
          }
          # ATTEMPT # condition = "input.tabselected==4", actionButton('test','Test')
        ) # close conditional panel
      }) # close renderUI
    

    【讨论】:

    • 您好文森特,按预期将“测试”操作按钮放置在选项卡 4 中,而不是选项卡 5 中。但是,在此更改之前,选项卡 4 中的任何修改(通过单击侧边栏中的“修改速率”操作按钮并更改模式对话框中的输入网格)都会立即反应性地反映在选项卡 4 和 5 中的两个表/图中。反之亦然。但是随着您的更改,在再次单击“修改费率”之前,不会在选项卡 5 中选择选项卡 4 中的修改,反之亦然。你知道如何恢复之前的反应吗?
    【解决方案3】:

    以下是解决此问题的完整 MWE 代码,同时仍使用 renderUIserver 部分呈现条件面板(也许我错了,将测试更多,但我认为这种 renderUI 方法解决了一些应用程序调用闪烁问题)。

    这是为我解决这个问题的条件面板的嵌套:

    output$Panels <- renderUI({
        conditionalPanel(
          condition = "input.tabselected==4 || input.tabselected==5", actionButton('modRates', 'Modify Rates'),
            conditionalPanel(
              condition = "input.tabselected==4", actionButton('test', 'Test'),
            ) # close 2nd conditional panel
        ) # close 1st conditional panel
      }) # close renderUI
    

    包含上述内容的完整 MWE:

    library(shiny)
    library(shinyMatrix)
    library(shinyjs)
    
    matrix4Default <- matrix(c(0.2), 4, 1, dimnames = list(c("A", "B", "C", "D"), NULL))
    
    matrix4Input <- function(x, matrix4Input) {
      matrixInput(
        x,
        value = matrix4Input,
        rows = list(extend = FALSE, names = TRUE),
        cols = list(
          extend = FALSE,
          names = FALSE,
          editableNames = FALSE
        ),
        class = "numeric"
      )
    }
    
    vectorBaseRate <- function(x, y) {
      a <- rep(y, x)
      b <- seq(1:x)
      c <- data.frame(x = b, y = a)
      return(c)
    }
    
    vectorBaseRatePlot <- function(w, x, y, z) {
      plot(
        w[, 1],
        sapply(w[, 2], function(x)
          gsub("%", "", x)),
        main = x,
        xlab = y,
        ylab = z
      )
    }
    
    ui <- pageWithSidebar(
      headerPanel("Model..."),
      sidebarPanel(fluidRow(helpText(h5(
        strong("Base Input Panel")
      ))), uiOutput("Panels")),
      mainPanel(tabsetPanel(
        tabPanel(
          "Liabilities module",
          value = 4,
          fluidRow(
            radioButtons(
              inputId = "showRates4",
              label = h5(strong(helpText(
                "Select model output to view:"
              ))),
              choices = c('Rates values', 'Rates plots'),
              selected = 'Rates values',
              inline = TRUE
            ),
            uiOutput('showTab4Results')
          )
        ),
        # close tab panel
        tabPanel(
          "Interest rates",
          value = 5,
          fluidRow(
            radioButtons(
              inputId = "showRates5",
              label = h5(strong(helpText(
                "Select model output to view:"
              ))),
              choices = c('Rates values', 'Rates plots'),
              selected = 'Rates values',
              inline = TRUE
            ),
            uiOutput('showTab5Results')
          )
        ),
        # close tab panel
        id = "tabselected"
      ))
    ) # close tabset panel, main panel, page with sidebar
    
    server <- function(input, output, session) {
      
      matrix4   <- reactive(input$matrix4)
      baseRate  <-
        function() {vectorBaseRate(60, input$matrix4[1, 1])} # Must remain in server section
      
      output$Panels <- renderUI({
        conditionalPanel(
          condition = "input.tabselected==4 || input.tabselected==5", actionButton('modRates', 'Modify Rates'),
            conditionalPanel(
              condition = "input.tabselected==4", actionButton('test', 'Test'),
            ) # close 2nd conditional panel
        ) # close 1st conditional panel
      }) # close renderUI
      
      vectorRates <- reactive({
        if (is.null(input$modRates)){DF <- NULL}
        else {
          if (input$modRates < 1) {DF <- cbind(Period = 1:60, BaseRate = 0.2)}
          else {
            req(input$matrix4)
            DF <- cbind(Period = 1:60, BaseRate = baseRate()[, 2])
          } # close 2nd else
        } # close 1st else
        DF
      }) # close reactive
      
      observeEvent(input$resetRatesStruct, {updateMatrixInput(session, 'matrix4', matrix4Default)})
      
      output$table5 <- output$table4 <- renderTable({vectorRates()})
      
      output$graph5 <- output$graph4 <- renderPlot({
        vectorBaseRatePlot(vectorRates(), "A Variable", "Period", "Rate")
      })
      
      output$showTab4Results <- renderUI({
        if (input$showRates4 == 'Rates values'){tableOutput("table4")} 
        else {plotOutput("graph4")}
      })
      
      output$showTab5Results <- renderUI({
        if (input$showRates5 == 'Rates values'){tableOutput("table5")} 
        else {plotOutput("graph5")}
      })
      
      observeEvent(input$modRates,
                   {
                     showModal(modalDialog(
                       matrix4Input("matrix4", if (is.null(input$matrix4))
                         matrix4Default
                         else
                           input$matrix4),
                       useShinyjs(),
                       footer = tagList(
                         actionButton("resetRatesStruct", "Reset"),
                         modalButton("Close")
                       )
                     ))
                   } # close modalDialog, showModal, and showModal function
      ) # close observeEvent
    } # close server
    
    shinyApp(ui, server)
    

    【讨论】:

      猜你喜欢
      • 2016-06-04
      • 2016-08-14
      • 2014-11-18
      • 2015-10-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多