R Shiny and DataTable (DT) Proxy Demonstration For Reactive Data Tables

In this R Shiny demonstration, we will be looking at an example where the user can manually add and remove data from a data table without rendering the entire DataTable again and again. Instead, we will be using the DataTable proxy (DT::dataTableProxy function) to replace and add data according to the user.

Let’s do it!

Shiny Application Set-Up

First, we will be loading the libraries.

library(shiny)
library(tidyverse)
library(DT)

And then we will be creating a data frame that shiny displays when the app first loads.

The data frame we will be using looks like that:

df <- dplyr::tibble(Height = "185", Weight = "95")

We are starting out with one row and two columns. The user should be able to add their height and weight to the table and then also removing rows without the need to render the data table every time.

R Shiny Data Table UI Components

For the UI components, we need 2 textinputs

  • shiny::textInput(inputId = "height", label = "height")
  • shiny::textInput(inputId = "weight", label = "weight")

For the first text input, the user can specify the height and in the second one their height. Next, we want to have an action button that initializes the appending to the data frame.

  • shiny::actionButton(inputId = "add", label = "Add")

Also, we need a drop-down menu that tells us what row number we want to remove plus one action button that initializes the removal of that particular row.

  • shiny::selectInput(inputId = "remove_row", label = "Remove Row", choices = 1:nrow(df))
  • shiny::actionButton(inputId = "remove", label = "Remove")

We are almost done with the UI! We only need the DataTable now.

  • DT::DTOutput(outputId = “table”)

That’s it for the UI. All in all, the UI looks like this:

ui <- fluidPage(
                
    # App title ----
    titlePanel("DT + Proxy + Replace Data"),
                
    # Sidebar layout with input and output definitions ----
    sidebarLayout(
                        
            # Sidebar panel for inputs ----
            sidebarPanel(
                                
                    # Input: Slider for the number of bins ----
                    shiny::textInput(inputId = "height", label = "height"),
                    shiny::textInput(inputId = "weight", label = "weight"),
                              
                    shiny::actionButton(inputId = "add", label = "Add"),
                                
                    shiny::selectInput(inputId = "remove_row", label = "Remove Row",
                                       choices = 1:nrow(df)),
                                
                    shiny::actionButton(inputId = "remove", label = "Remove")
                                
            ),
                        
            # Main panel for displaying outputs ----
            mainPanel(
                                
                    # Output: Histogram ----
                    DT::DTOutput(outputId = "table")
                               
        )
    )
)

 

R Shiny DataTable Server Components – Proxy + ReplaceData

The server logic is a bit more complicated because we are using reactivity. The user has the chance to update the DataTable reactively/manually.

First, we will be creating reactive values and then rendering the DataTable consisting of one row and two columns.

mod_df <- shiny::reactiveValues(x = df)
        
output$table <- DT::renderDT({
                
    isolate(mod_df$x)
                
}, options = list(paging = FALSE, processing = FALSE))

We use isolate in the code above because we do not want to have a dependency between the data and the DataTable render function. At the start, the entire table should be loaded, however, beyond that theDataTable should only get updated, instead of rendered every time the user adds or removes a row.

Next, we have to update the drop-down menu.  Whenever the user adds a row, we have to add one more row to the drop-down menu, and when the user removes a row, we have to remove a row from the drop-down menu. We do that with updateSelectInput.

shiny::observe({
    shiny::updateSelectInput(session, inputId = "remove_row",
                             choices = 1:nrow(mod_df$x))
})

Next, we use observeEvent to make use of the action buttons.

shiny::observeEvent(input$add, {
                
    mod_df$x <- mod_df$x %>%
        dplyr::bind_rows(
            dplyr::tibble(Height = input$height,
                          Weight = input$weight)
            )
                
})
        
shiny::observeEvent(input$remove, {
                
    mod_df$x <- mod_df$x[-as.integer(input$remove_row), ]
                
})

After that, the magic with data table proxy happens, and with the replaceData function in the DT package.

proxy <- DT::dataTableProxy('table')
shiny::observe({
                
    DT::replaceData(proxy, mod_df$x)
                
})

And we are done. Now, every time the user adds or removes a row, the proxy is used to update the data instead of using the data table render function to render the entire data. This is especially critical when we have large amounts of data. Using the proxy is way more efficient and increases the speed of your R Shiny application by a lot!

Full R Shiny Application Code

library(shiny)
library(tidyverse)
library(DT)

df <- dplyr::tibble(Height = "185", Weight = "95")

ui <- fluidPage(
                
    # App title ----
    titlePanel("DT + Proxy + Replace Data"),
                
        # Sidebar layout with input and output definitions ----
        sidebarLayout(
                        
            # Sidebar panel for inputs ----
            sidebarPanel(
                                
                # Input: Slider for the number of bins ---- 
                shiny::textInput(inputId = "height", label = "height"),
                shiny::textInput(inputId = "weight", label = "weight"),
                                
                shiny::actionButton(inputId = "add", label = "Add"),
                                
                shiny::selectInput(inputId = "remove_row", label = "Remove Row",
                                   choices = 1:nrow(df)),
                                
                shiny::actionButton(inputId = "remove", label = "Remove")
                                
            ),
                        
            # Main panel for displaying outputs ----
            mainPanel(
                                
                # Output: Histogram ----
                DT::DTOutput(outputId = "table")
                                
        )
    )
)
        


# Define server logic required to draw a histogram ----
server <- function(input, output, session) {
        
        mod_df <- shiny::reactiveValues(x = df)
        
        output$table <- DT::renderDT({
                
                isolate(mod_df$x)
                
        })
        
        shiny::observe({
                shiny::updateSelectInput(session, inputId = "remove_row",
                                         choices = 1:nrow(mod_df$x))
        })
        
        shiny::observeEvent(input$add, {
                
                mod_df$x <- mod_df$x %>%
                        dplyr::bind_rows(
                                dplyr::tibble(Height = input$height,
                                              Weight = input$weight)
                        )
                
        })
        
        shiny::observeEvent(input$remove, {
                
                mod_df$x <- mod_df$x[-as.integer(input$remove_row), ]
                
        })
        
        proxy <- DT::dataTableProxy('table')
        shiny::observe({
                
                DT::replaceData(proxy, mod_df$x)
                
        })
        
        
}

shinyApp(ui, server)

The R Shiny application can be found here.

Additional Resources

  • A great blog post about DataTable proxy and editing tables after rendering can be found here.
  • Another post, that talks about the same DataTable and replaceData can be found here.
  • Here is another interesting read that is more theoretical.
  • This blog post uses the DTedit library for updating DataTables.
  • Another great article, that provides the source code, some explanation, and a shiny application for demonstration purposes on how to reactively edit DataTables in an R Shiny application.

 

If you have any questions or suggestions, let me know in the comments below.

Post your comment