Почему необходимо сначала вызвать это реактивное значение, прежде чем его можно будет использовать? - PullRequest
0 голосов
/ 13 июля 2020

Пытаясь придумать слишком сложное решение для этого вопроса , я наткнулся на следующую проблему. У меня есть 2 разных модуля, inputtable отображает rhandsontable и возвращает эту таблицу, а resulttable принимает эту таблицу в качестве входных данных, выполняет некоторые вычисления и отображает результат как rhandsontable. inputtable возвращает объект rhandsontable как реактивное значение. Поскольку может быть несколько копий каждого модуля, я сохраняю результаты из inputtable в списке и передаю элементы списка в качестве входных в resulttable.

Я заметил, что в resulttable, реактивный вход input_data$input_table() не может использоваться напрямую. Когда я вызываю функцию browser или print или назначаю ее переменной до того, как использую реактивное значение для фактической цели, тогда она работает. В противном случае я получаю сообщение об ошибке

попытка применить нефункцию

Насколько я понял, передача реактивных значений в модули, это должно работать без необходимости делать что-то еще до реактивного значения перед его использованием. Если я не использую список для хранения реактивного значения, а использую только одну копию каждого модуля и напрямую сохраняю результат inputtable в переменной и передаю его в resulttable, он работает так, как я ожидал. (Но сохранение различных реактивных значений в объекте reactiveValues также приводит к ошибке.)

Кто-нибудь знает, что там происходит?

Прошу прощения за длинный пример, когда я пытался сократить, я потерял ошибку:

library(shiny)
library(rhandsontable)

# define the first module
resulttableUI <- function(id) {
  ns <- NS(id)
  tabPanel(title = ns(id),
           column(12,
                  rHandsontableOutput(ns("results_table"))))
}

resulttable <- function(id, input_data) {
  moduleServer(
    id,
    function(input, output, session) {
      # THE NEXT LINE NEEDS TO BE UNCOMMENTED TO MAKE IT WORK
      # used_data <- input_data$input_table()
      output$results_table <- renderRHandsontable({
        rhandsontable(hot_to_r(input_data$input_table())[2:5]/hot_to_r(input_data$input_table())[1:4])
      })
    }
  )
}

# define the second module
inputtableUI <- function(id) {
  ns <- NS(id)
  tabPanel(title = ns(id),
           column(12,
                  rHandsontableOutput(ns("input_table"))))
}

inputtable <- function(id, i) {
  moduleServer(
    id,
    function(input, output, session) {
      output$input_table <- renderRHandsontable({
        mat <- matrix(c(1:25) * i, ncol = 5, nrow = 5)
        mat <- as.data.frame(mat)
        rhandsontable(mat)
      })
      
      return(list(
        input_table = reactive({input$input_table})
      ))
    }
  )
}

ui <- navbarPage("App",
                 
                 tabPanel("Input",
                          numericInput('num_of_table', "Number of sub tabs: ", value = 1, min = 1, max = 10),
                          tabsetPanel(id = "insert_input")),
                 tabPanel("Results",
                          tabsetPanel(id = "insert_results"))
                 
)

number_modules <- 0
current_id <- 1

server <- function(input, output, session) {
  
  # variable to store the inputs from the modules
  input_data <- list()
  
  observeEvent(input$num_of_table, {
      modules_to_add <- input$num_of_table - number_modules
      for (i in seq_len(modules_to_add)) {
        # add the logic for the input
        input_data[[paste0("inputtable_", current_id)]] <<-
          inputtable(paste0("inputtable_", current_id), current_id)
        # add the logic for the results
        resulttable(paste0("resulttable_", current_id),
                    input_data = input_data[[paste0("inputtable_", current_id)]])
        
        # add the UI
        appendTab(inputId = "insert_input",
                  tab = inputtableUI(paste0("inputtable_", current_id)))
        appendTab(inputId = "insert_results",
                  tab = resulttableUI(paste0("resulttable_", current_id)))
        # update the id
        current_id <<- current_id + 1
        
      }
      
      number_modules <<- input$num_of_table
    
    updateTabsetPanel(session,
                      "insert_input",
                      "inputtable_1-inputtable_1")

  })
}


shinyApp(ui,server)

Я использую R 3.6.1 и shiny 1.5.0.

К сожалению, есть еще 2 проблемы:

  • Вам нужно щелкнуть вкладку результатов, чтобы отобразить его.
  • Первая показанная таблица из модуля inputtable использует i = 2 вместо i = 1, я еще не понял почему.

Возможно, с моим кодом что-то не так. Я рад любым подсказкам об этом странном поведении или о том, как сделать более простой пример.

1 Ответ

1 голос
/ 14 июля 2020

Изменяя for l oop на lapply и некоторые другие незначительные изменения в функции сервера, я думаю, что это работает. Попробуйте это

ui <- fluidPage(navbarPage("App",
                 
                 tabPanel("Input",
                          sliderInput('num_of_table', "Number of sub tabs: ", value = 1, min = 1, max = 10),
                          #numericInput('num_of_table', "Number of sub tabs: ", value = 1, min = 1, max = 10),
                          tabsetPanel(id = "insert_input")),
                 tabPanel("Results",
                          tabsetPanel(id = "insert_results"))
                 
))

#number_modules <- 0
current_id <- 0

server <- function(input, output, session) {
  number_modules <- reactiveVal(0)
  # variable to store the inputs from the modules
  input_data <- list()
  
  observeEvent(input$num_of_table, {
    req(input$num_of_table)
    if (input$num_of_table > number_modules() ){
      modules_to_add <- reactive({input$num_of_table - number_modules()})
    }else {
      modules_to_add <- reactive({0})
    }
    lapply(1:modules_to_add(), function(i) {
      # update the id
      current_id <<- current_id + 1
      input_data[[paste0("inputtable_", current_id)]] <<-
        inputtable(paste0("inputtable_", current_id), current_id)
      # add the logic for the results
      resulttable(paste0("resulttable_", current_id),
                  input_data = input_data[[paste0("inputtable_", current_id)]])
      
      ## add the UI
      if (input$num_of_table > number_modules() ){
        appendTab(inputId = "insert_input",
                  tab = inputtableUI(paste0("inputtable_", current_id)))
        appendTab(inputId = "insert_results",
                  tab = resulttableUI(paste0("resulttable_", current_id)))
      }
      
    })
    
    if (input$num_of_table > number_modules() ){
      number_modules(input$num_of_table)
      updateTabsetPanel(session,
                        "insert_input",
                        "inputtable_1-inputtable_1")
    }
    
  })
}

Возможно, все еще потребуется обновление того, что отображать, поскольку таблица ввода отображает последнюю таблицу для всех вложенных вкладок, если для num_of_table выбрано большое число.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...