Search code examples
rshinyshinymodules

User edit one table and the change is reflected in another table


I have this problem and the closest I can find is in reference to the submission here but it doesn't quite address what I'm trying to solve Reactive shiny modules sharing data

Referring to the corrected example in the link above, what if I want to be able to edit table a (cells in column x_2), and this will automatically update table c (the corresponding cells in column x_2).

Thanks


Solution

  • Here is a bit simpler version that doesn't work with proxies (and uses the new module interface), I hope it is ok. You can change any value in the first 2 tables and the 3rd table shows the sum and is updated. The trick is that the modules where you edit data have to return the edited data as reactives, these are saved as variables in the main server function. The module that updates based on this data needs to take these variables as reactive inputs.

    Very important is:

    • the modules that return data need to return a reactive, the easiest way to do this is return(reactive({returnvalue}))
    • in the server function the reactives passed to the module mustn't be evaluated, e.g. you have to use my_reactive_value instead of my_reactive_value()
    ### Libraries
    library(shiny)
    library(dplyr)
    library(DT)
    
    ### Data----------------------------------------
    set.seed(4)
    table_a <- data.frame(
      id=seq(from=1,to=10),
      x_1=rnorm(n=10,mean=0,sd=10),
      x_2=rnorm(n=10,mean=0,sd=10),
      x_3=rnorm(n=10,mean=0,sd=10)
    ) %>% 
      mutate_all(round,3)
    
    table_b <- data.frame(
      id=seq(from=1,to=10),
      x_1=rnorm(n=10,mean=0,sd=10),
      x_2=rnorm(n=10,mean=0,sd=10),
      x_3=rnorm(n=10,mean=0,sd=10)
    )%>% 
      mutate_all(round,3)
    
    mod_table_edit <- function(id, data_initialisation) {
      moduleServer(
        id,
        function(input, output, session) {
          # initialise the reactive data object for the table
          data <- reactiveValues(table = data_initialisation)
          
          # render the table
          output$table <- renderDT({
            datatable(data$table,
                      editable = TRUE)
          })
          
          # update the underlying data
          observeEvent(input$table_cell_edit, {
            data$table <- editData(data$table, input$table_cell_edit)
          })
          
          # return the data as a reactive
          return(reactive(data$table))
        }
      )
    }
    
    mod_table_add <- function(id, data_input_1, data_input_2) {
      moduleServer(
        id,
        function(input, output, session) {
          # do the calculations
          data_table <- reactive({
            data_input_1() + data_input_2()
          })
          
          # render the table
          output$table <- renderDT({
            datatable(data_table())
          })
        }
      )
    }
    
    modFunctionUI <- function(id) {
      ns <- NS(id)
      DTOutput(ns("table"))
    }
    
    ui <- fluidPage(
      fluidRow(
        column(4,
               modFunctionUI("table_1")),
        column(4,
               modFunctionUI("table_2")),
        column(4,
               modFunctionUI("table_3"))
      )
    )
    
    server <- function(input, output, session) {
      # call the modules for the editable tables and store the results
      data_table_1 <- mod_table_edit("table_1", data_initialisation = table_a)
      data_table_2 <- mod_table_edit("table_2", data_initialisation = table_b)
      
      # call the module for the table that takes inputs
      # the reactives musn't be evaluated
      mod_table_add("table_3",
                    data_input_1 = data_table_1,
                    data_input_2 = data_table_2)
    }
    
    shinyApp(ui, server)