rshinymoduleshinyapps

How do I make a simple user-editable table in an R Shiny app using modular design?


I see some solutions with DT, but this is really kind of overkill and I want the easy ability for the user to add/ delete and move rows around and be able to access these manipulations for other parts of my app.

I would also like to implement this as a reusable module to cut down on code complexity. A simple example with mtcars[1:5,] would be great.


Solution

  • I ended up piecing together a solution using the rhandsontable library. Here is an example with moveable rows and editable cells connected to tabular output that can easily be fed and interpreted in the R environment: enter image description here

    library(shiny)
    library(dplyr)
    require(rhandsontable)
    
    
    
    #------
    
    #Definition of UI part of ediTable module
    
    ediTable <- function(id, ...) {
      ns <- NS(id)
      rHandsontableOutput(outputId = ns("hot"), ...)
      
    }
    
    #Server logic for the module
    
    ediTable_server <-
      function(id,
               rd,
               allowRowEdit = TRUE,
               allowColumnEdit = FALSE,
               manualRowMove = TRUE,
               ...) {
        moduleServer(id,
                     function(input, output, session) {
                       output$hot <- renderRHandsontable({
                         tmp <- isolate(rd())#Gotta isolate it or it'll cause infinite loop
                          #Necessary to avoid the issue described [here](https://github.com/jrowen/rhandsontable/issues/166)
                         rownames(tmp) <- NULL
                         rhandsontable(
                           tmp,
                           allowRowEdit = allowRowEdit,
                           allowColumnEdit = allowColumnEdit,
                           manualRowMove = manualRowMove,
                           ...
                         )
                         
                       })
                       
                       #Update the reactive values for this user-manipulated data to pass back to main environment
                       observeEvent(input$hot, {
                         
                         tmp <- rhandsontable::hot_to_r(input$hot)
                         
                         rd(tmp)
                         
                         
                       })
                       
                     })
      }
    
    
    
    # Define UI for the main application
    ui <- fluidPage(
      # Application title
      titlePanel("ediTable: editable table widget"),
      
      # Sidebar with a slider input for number of bins
      sidebarLayout(
        sidebarPanel(
          h3("Motivation:"),
          p(
            "Sometimes you just want to allow users to edit a table and then save that input data."
          ),
          p(
            "This is basically a shallow, more intuitive wrapper for rhandsontable."
          )
        ),
        
        # Show a plot of the generated distribution
        mainPanel(
          h1("Table 1: a reactive module"),
          p("Double click a cell to edit."),
          p("Right click to add or remove rows; click and drag to move."),
          ediTable(id = "tab"),
          h3("Outputting the edited data for Table 1"),
          tableOutput("data1"),
          h1("Table 2: independently reactive module"),
          ediTable(id = "tab2"),
          h3("Outputting the edited data for Table 1"),
          tableOutput("data2")
          
          
        )
      )
    )
    
    # Define server logic for the main application
    server <- function(input, output) {
      init_data <- head(mtcars)
      reactive_data1 <-  reactiveVal(init_data)
      reactive_data2 <-  reactiveVal(init_data)
      
      ediTable_server(id = "tab", rd = reactive_data1)
      
      ediTable_server(id = "tab2",  rd = reactive_data2)
      
      observe({
        tmp <- reactive_data1()
        output$data1 <-
        tmp %>% renderTable()
      
      })
      observe({
       tmp2 <- reactive_data2()
      output$data2 <- tmp2 %>% renderTable()
        })
    
      
    }
    
    # Run the application
    shinyApp(ui = ui, server = server)