反应值与反应;从 tabPanel [R Shiny] 延迟加载

reactiveValues vs reactive; lazy loading from tabPanel [R Shiny]

我在下面有一个最小代表。我有两个选项卡,我希望数据仅在用户单击第二个选项卡时加载到第二个选项卡中。第二个选项卡中的实际数据来自 API,所以我只希望它在单击时加载(而不是每次加载仪表板时)。

我希望加载数据并通过向数据集附加一行来让用户选择添加数据。

对于这个代表,我使用了鸢尾花数据集。我使用过 reactiveValues,除了一个问题外,这似乎工作正常。它不会延迟加载,虹膜数据集会在仪表板加载时加载(无需导航到第二个选项卡)。

library(shiny)
library(dplyr)

ui <- fluidPage(

  navlistPanel(
    tabPanel(
      title = "Main Page" # Empty
    )
    ,tabPanel(
      title = "Iris"
      ,fluidRow(
        column(
          width = 6
          ,uiOutput(outputId = "choose_species")
        )
        ,column(
          width = 6
          ,uiOutput(outputId = "add_species")
          ,uiOutput(outputId = "add_measure")
          ,uiOutput(outputId = "ok")
        )
      )
      ,fluidRow(
        column(
          width = 6
          ,verbatimTextOutput(outputId = "print_df")
        )
      )
    )

  )

)


server <- function(input, output) {

  df <- reactiveValues(iris_df = NULL)

  observe({
    print(is.null(df$iris_df))
  })

  df$iris_df <- iris %>% 
    mutate(Species = as.character(Species))

  observe({
    print(is.null(df$iris_df))
  })



  output$choose_species <- renderUI({

    selectInput(
      inputId = "input_choose_species"
      ,label = "Choose Species"
      ,choices = df$iris_df %>% distinct(Species)
    )

  })

  output$add_species <- renderUI({

    textInput(
      inputId = "input_add_species"
      ,label = "Add Species"
      ,value = ""
    )

  })

  output$add_measure <- renderUI({

    numericInput(
      inputId = "input_add_measure"
      ,label = "Add Measurements"
      ,value = ""
    )

  })

  output$ok <- renderUI({

    actionButton(
      inputId = "input_ok"
      ,label = "Add New Species"
    )

  })

  observeEvent(input$input_ok, {

    req(
      input$input_add_species
      ,input$input_add_measure
    )

    new_row <- c(rep(input$input_add_measure, 4), input$input_add_species)

    df$iris_df <- df$iris_df %>% rbind(new_row)

  })

  output$print_df <- renderPrint({

    req(input$input_choose_species)

    df$iris_df %>%
      filter(Species == input$input_choose_species)

  })

}

shinyApp(ui = ui, server = server)

我曾尝试通过使用 reactive() 调用来解决这个问题,但现在我却收到了这个错误:

server <- function(input, output) {

  df <- reactive({

    iris %>% 
       mutate(Species = as.character(Species))

    })


  output$choose_species <- renderUI({

    selectInput(
      inputId = "input_choose_species"
      ,label = "Choose Species"
      ,choices = df() %>% distinct(Species)
    )

  })

  output$add_species <- renderUI({

    textInput(
      inputId = "input_add_species"
      ,label = "Add Species"
      ,value = ""
    )

  })

  output$add_measure <- renderUI({

    numericInput(
      inputId = "input_add_measure"
      ,label = "Add Measurements"
      ,value = ""
    )

  })

  output$ok <- renderUI({

    actionButton(
      inputId = "input_ok"
      ,label = "Add New Species"
    )

  })

  df <- eventReactive(input$input_ok, {

    req(
      input$input_add_species
      ,input$input_add_measure
    )

    new_row <- c(rep(input$input_add_measure, 4), input$input_add_species)

    df() %>% rbind(new_row)

  })

  output$print_df <- renderPrint({

    req(input$input_choose_species)

    df() %>%
      filter(Species == input$input_choose_species)

  })

}

shinyApp(ui = ui, server = server)

Warning: Error in : evaluation nested too deeply: infinite recursion / options(expressions=)?
  [No stack trace available]

我想我已经很接近了,可能遗漏了一些非常明显的东西。 TIA

我认为应该可以让它与 reactive() 一起工作,但是当根据其自身的值修改反应式表达式时,很容易创建无限循环。 另一种方法是使用 observeEvent() 来延迟创建 reactiveValue。

library(shiny)
library(dplyr)

ui <- fluidPage(

  navlistPanel(id = 'tabs', # set id to allow the server to react to tab change
               tabPanel(title = "Main Page" # Empty
               )
               ,tabPanel(title = "Iris" # Title is value if no value is set
                         ,fluidRow(
                           column(
                             width = 6
                             ,uiOutput(outputId = "choose_species")
                           )
                           ,column(
                             width = 6
                             ,uiOutput(outputId = "add_species")
                             ,uiOutput(outputId = "add_measure")
                             ,uiOutput(outputId = "ok")
                           )
                         )
                         ,fluidRow(
                           column(
                             width = 6
                             ,verbatimTextOutput(outputId = "print_df")
                           )
                         )
               )


  )

)


server <- function(input, output) {

  df = reactiveVal()

  observeEvent(input$tabs, {
    req(is.null(df()))
    if (input$tabs == 'Iris') df(mutate(iris, Species = as.character(Species)))
  })


  output$choose_species <- renderUI({
    req(df())

    selectInput(
      inputId = "input_choose_species"
      ,label = "Choose Species"
      ,choices = df() %>% distinct(Species)
    )

  })

  output$add_species <- renderUI({

    textInput(
      inputId = "input_add_species"
      ,label = "Add Species"
      ,value = ""
    )

  })

  output$add_measure <- renderUI({

    numericInput(
      inputId = "input_add_measure"
      ,label = "Add Measurements"
      ,value = ""
    )

  })

  output$ok <- renderUI({

    actionButton(
      inputId = "input_ok"
      ,label = "Add New Species"
    )

  })

  observeEvent(input$input_ok, {

    req(
      input$input_add_species
      ,input$input_add_measure
    )

    new_row <- c(rep(input$input_add_measure, 4), input$input_add_species)

    df(df() %>% rbind(new_row))

  })

  output$print_df <- renderPrint({

    req(input$input_choose_species)

    df() %>%
      filter(Species == input$input_choose_species)

  })

}

shinyApp(ui = ui, server = server)

另一种解决方案是替换您的

  df$iris_df <- iris %>% 
    mutate(Species = as.character(Species))

与下面。

  observeEvent(input$tabs == "Iris", 
               {
                 df$iris_df <- iris %>% 
                   mutate(Species = as.character(Species))
                 print("Loaded Iris")
               },
               ignoreInit = TRUE,
               once = TRUE
  )

如您在控制台中所见,这会导致数据集在选项卡更改时加载,并且仅加载一次。