闪亮的模块:如果服务器功能失败,则销毁模块ui

时间:2019-11-18 11:29:23

标签: r shiny shinymodules

如果模块服务器功能失败,如何在不将所有UI代码移动到服务器功能的情况下显示空白的UI(或者破坏模块UI)?

简单的可复制示例:

library(shiny)

my_module_ui <- function(id) {
  ns <- NS(id)
  tags$div(
    tags$h1("Don't show me if my_module_server fails!"),
    plotOutput(ns("my_plot"))
  )
}

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

  tryCatch({
    my_data <- cars * "A" # fail for demo
    # my_data <- cars

    output$my_plot <- renderPlot({
      cars2 <- my_data + rnorm(nrow(my_data))
      plot(cars2)
    })
  }, error=function(cond) {
    message("Destroy UI here!")
  })


}

ui <- fluidPage(
  my_module_ui("my_id")
)

server <- function(input, output, session) {
  callModule(my_module_server, "my_id")
}

shinyApp(ui, server)

我当前的解决方案是在uiOutput()中只保留my_module_ui,然后在服务器函数中呈现整个ui。我想避免这种情况,因为如果所有UI组件都放在模块服务器功能内,大型模块会变得非常混乱。

此外,我最好还是避免从callModule()返回破坏UI的值,而应从服务器功能中执行此操作。

谢谢!

2 个答案:

答案 0 :(得分:2)

在创建UI之前,如何为会话对象分配一个值并评估该值(从服务器端通过renderUI()

1)将UI呈现移到服务器端

在服务器端使用renderUI(my_module_ui("my_id")),在ui端使用uiOutput("module")

2)要检测服务器模块是否成功,请为会话对象分配一个值

my_module_server <- function(input, output, session) {
  tryCatch({
     ...
    session$userData$mod_server <- TRUE
  }, error = function(cond) {
    session$userData$mod_server <- NULL
  })
}

3)使用此值可以有条件地对模块ui进行调用

  output$module <- renderUI({
    callModule(my_module_server, "my_id")
    if(!is.null(session$userData$mod_server)) my_module_ui("my_id")
  })

可复制的示例:

library(shiny)

my_module_ui <- function(id) {
  ns <- NS(id)
  tags$div(
    tags$h1("Don't show me if my_module_server fails!"),
    plotOutput(ns("my_plot"))
  )
}

my_module_server <- function(input, output, session) {
  tryCatch({
    my_data <- cars * "A" # fail for demo
    # my_data <- cars

    output$my_plot <- renderPlot({
      cars2 <- my_data + rnorm(nrow(my_data))
      plot(cars2)
    })
    session$userData$mod_server <- TRUE
  }, error = function(cond) {
    session$userData$mod_server <- NULL
  })
}

ui <- fluidPage(
  uiOutput("module")
)

server <- function(input, output, session) {
  output$module <- renderUI({
    callModule(my_module_server, "my_id")
    if(!is.null(session$userData$mod_server)) my_module_ui("my_id")
  })
}
shinyApp(ui, server)

答案 1 :(得分:1)

只需对代码进行一点重新排序,并使用令人惊叹的shinyjs package即可。

请注意,我添加了一个输入来模拟错误而不是错误,以查看UI消失的方式。同样,所有操作都在模块的服务器部分完成。我希望这能帮到您。该代码具有解释这些步骤的内联注释。

library(shiny)
library(shinyjs)

my_module_ui <- function(id) {
  ns <- NS(id)

  tagList(
    # input added to be able to throw errors and see the ui dissapear
    selectInput(
      ns('trigger'), 'Error trigger',
      choices = list('no error' = c(2,1), 'error' = c('A', 'B')),
      selected = 2
    ),
    tags$div(
      # div with id, to select it with shinyjs and hide it if necessary
      id = ns('hideable_div'),
      tags$h1("Don't show me if my_module_server fails!"),
      plotOutput(ns("my_plot"))
    )
  )
}

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

  # get all the things prone to error in a reactive call, that way you capture the final
  # result or a NULL reactive when an error occurs
  foo <- reactive({

    tryCatch({

      if (input$trigger %in% c(2,1)) {
        trigger <- as.numeric(input$trigger)
      } else {
        trigger <- input$trigger
      }

      cars * trigger
    }, error=function(cond) {
      message("Destroy UI here!")
    })
  })

  # obseveEvent based on the error reactive, to check if hide or not the UI
  observeEvent(foo(), {
    # hide checking if foo is null, using shinyjs
    if (is.null(foo())) {
      shinyjs::hide('hideable_div')
    } else {
      shinyjs::show('hideable_div')
    }
  }, ignoreNULL = FALSE, ignoreInit = FALSE)


  # outputs, with validation of the error reactive. That way code after validate is not
  # executed but the app does not get blocked (gray)
  output$my_plot <- renderPlot({
    shiny::validate(
      shiny::need(foo(), 'no data')
    )
    cars2 <- foo() + rnorm(nrow(foo()))
    plot(cars2)
  })

}

ui <- fluidPage(
  # really important for shinyjs tu work!!!!!!!
  shinyjs::useShinyjs(),
  my_module_ui("my_id")
)

server <- function(input, output, session) {
  callModule(my_module_server, "my_id")
}

shinyApp(ui, server)