add a button into a row in a datatable

shiny

#1

I am trying to add a button into a row in a table.
The table is in tab1. When the button is clicked, a message is shown.
I am following this tutorial, but it doesn't work.
https://antoineguillot.wordpress.com/2017/03/01/three-r-shiny-tricks-to-make-your-shiny-app-shines-33-buttons-to-delete-edit-and-compare-datatable-rows/

library(shiny)
library(ggplot2)  # for the diamonds dataset
library(DT)
library(ROracle)
library(DBI)
library(shinyjs)
library(shinydashboard)
library(data.table)



ui <- fluidPage(
  title = "Examples of DataTables",
  mainPanel(
    tabsetPanel(
      id = 'dataset',
      tabPanel("tab 1", DT::dataTableOutput("tab1")),
      tabPanel("tab 2", DT::dataTableOutput("tab2")),
      tabPanel("tab 2", DT::dataTableOutput("tab3"))
    )
  ),
 
  tags$script("$(document).on('click', '#Main_table button', function () {
              Shiny.onInputChange('lastClickId',this.id);
              Shiny.onInputChange('lastClick', Math.random())
              });")
  
)

server <- function(input, output) {
  vals<-reactiveValues()
  vals$Data<-data.table(
    Brands=paste0("Brand",1:10),
    Forecasted_Growth=sample(1:20,10),
    Last_Year_Purchase=round(rnorm(10,1000,1000)^2),
    Contact=paste0("Brand",1:10,"@email.com")
  )
  
  
  output$tab1 <- DT::renderDataTable({
    DT=vals$Data
    DT[["Actions"]]<-
      paste0('
             <div class="btn-group" role="group" aria-label="Basic example">
             <button type="button" class="btn btn-secondary delete" id=delete_',1:nrow(vals$Data),'>Delete</button>
             </div>
             
             ')
    datatable(DT, escape=F)})
  
  observeEvent(input$lastClick,
               {
                 if (input$lastClickId%like%"delete")
                 {
                    print("DELETE CLICKED")              
                 }
               }
  )
  
  
               
}


shinyApp(ui, server)


#2

This could be achieved through a function within Server to create Inputs as mentioned here (in this case: Delete button for each table rows).

The buttonInput function creates Inputs of type actionButton for a specified length (length = number of rows in vals$Data) and is applied to create Action column in reactive data.table(). Note that the 'Shiny.onInputChange' can be assigned to onClick parameter. The observeEvent on Delete button click stores the row ID to which the clicked button belongs to which is later used to print the corresponding Brands.

Below is the example:

library(shiny)
library(ggplot2)
library(DT)
library(DBI)
library(shinyjs)
library(shinydashboard)
library(data.table)



ui <- fluidPage(
  title = "Examples of DataTables",
  mainPanel(tabsetPanel(
    id = 'dataset',
    tabPanel("tab 1", DT::dataTableOutput("tab1"), verbatimTextOutput('printMsg')),
    tabPanel("tab 2", DT::dataTableOutput("tab2")),
    tabPanel("tab 2", DT::dataTableOutput("tab3"))
  ))

)

server <- function(input, output) {
  
  printText <- reactiveValues(brands = '')
  
  buttonInput <- function(FUN, len, id, ...) {
    inputs <- character(len)
    for (i in seq_len(len)) {
      inputs[i] <- as.character(FUN(paste0(id, i), ...))
    }
    inputs
  }
  
  vals <- reactiveValues()
  
  vals$Data <- data.table(
    Brands = paste0("Brand", 1:10),
    Forecasted_Growth = sample(1:20, 10),
    Last_Year_Purchase = round(rnorm(10, 1000, 1000) ^ 2),
    Contact = paste0("Brand", 1:10, "@email.com"),
    
    Action = buttonInput(
      FUN = actionButton,
      len = 10,
      id = 'button_',
      label = "Delete",
      onclick = 'Shiny.onInputChange(\"lastClick\",  this.id)'
    )
  )


  output$tab1 <- DT::renderDataTable({
    DT = vals$Data
    datatable(DT, escape = F)
  })
  
  observeEvent(input$lastClick, {
    selectedRow <- as.numeric(strsplit(input$lastClick, "_")[[1]][2])
    printText$brands <<- paste('clicked on ',vals$Data[selectedRow,1])
  })
  
  output$printMsg <- renderText({
    printText$brands
  })

}

shinyApp(ui, server)

#3

You seem to have solved the issue of how to get dataTable to display buttons in each row.

The bigger problem is that a separate observer is required for each actionButton. If you don't know how many rows will be in your table ahead of time, you don't know how many actionButton observers you'll need. It's also a bit crazy to have multiple observers when only one would do.

Having one observer for all buttons requires a tiny bit of javascript, but I've documented all the details about how to set this up are in this blog post..

Another feature of this solution is that it also works for links that use ids rather than hrefs (<a id="xyz">).