javascriptrshinydtshinymodules

How to create a JS event for a button in a DataTable using modules?


I wanted to insert buttons into a DataTable and found this very helpful code. Just copy pasting it in the simplest case works like a charm, however, I cannot adapt it to my modularized situation. I am attaching my code with some very minor adaptations to keep everything shorter. None of this should lead the code to break though, as I have tested the same adaptations on the non-modularized variant (no intermediate data frame, simpler data retrieval process, no abstracted button generator function).

library(shiny)
library(DT)
library(tibble)

uiModuleImpl <- function(id) {
  ns <- NS(id)
  
  tabPanel(
    title = "Dummy title",
    DTOutput(ns("dummyDT")),
    textOutput(ns("myText")),

    value = id
  )
}

serverModuleImpl <- function(id) {
  moduleServer(id, function(input, output, session) {
    ns <- session$ns
    
    output$dummyDT <- renderDT({
      dummyData <- tibble(
        "Col A" = c(2000, 1994),
        "Col B" = c(2019, 2023),
      )

      dummyData$Calculate <- vapply(X=1:2, function(i) {
        as.character(
          actionButton(
            inputId = ns(paste0("actionButton_", i)),
            label = "Calculate",
            onclick = 'Shiny.setInputValue(\"idxToProcess\", i, {priority: \"event\"})')) # does not work
            # onClick = sprintf('Shiny.setInputValue(\"%s\", i, {priority: \"event\"})', ns("idxToProcess")))) # tried some variants of accessing the namespace, also does not work
      }, character(1))
      
      dummyData
    },
    
      escape = F,
      selection = "none"
    )
    
    observe({
      print(paste("idx =", input$idxToProcess))
    })

    output$myText <- renderText({
      req(input$idxToProcess)
      paste("idx =", input$idxToProcess)
    })
  })
}

ui <- fluidPage(
  uiModuleImpl("someNamespace")
)

server <- function(input, output, session) {
  serverModuleImpl("someNamespace")
}

shinyApp(ui = ui, server = server)

The solution is probably simply that the onClick event writes into the wrong namespace. I am a very inexperienced JS developer though and have not managed to find just where exactly I would find this variable, though.


Solution

  • You can use sprintf() with two replacements like this:

    onclick = sprintf(
      'Shiny.setInputValue(%s, %f, {priority: \"event\"})',
      paste0('"', ns("idxToProcess"), '"'),
      i
    )
    

    enter image description here

    library(shiny)
    library(DT)
    library(tibble)
    
    uiModuleImpl <- function(id) {
      ns <- NS(id)
      
      tabPanel(
        title = "Dummy title",
        DTOutput(ns("dummyDT")),
        textOutput(ns("myText")),
        value = id
      )
    }
    
    serverModuleImpl <- function(id) {
      moduleServer(id, function(input, output, session) {
        ns <- session$ns
      
        
        output$dummyDT <- renderDT({
          dummyData <- tibble("Col A" = c(2000, 1994),
                              "Col B" = c(2019, 2023),)
          
          dummyData$Calculate <- vapply(X = 1:2, function(i) {
            as.character(actionButton(
              inputId = ns(paste0("actionButton_", i)),
              label = "Calculate",
              onclick = sprintf(
                'Shiny.setInputValue(%s, %f, {priority: \"event\"})',
                paste0('"', ns("idxToProcess"), '"'),
                i
              )
            ))
          }, character(1))
          
          dummyData
        }, escape = F, selection = "none")
            
        output$myText <- renderText({
          paste("idx =", input$idxToProcess)
        })
      })
    }
    
    ui <- fluidPage(
      uiModuleImpl("someNamespace")
    )
    
    server <- function(input, output, session) {
      serverModuleImpl("someNamespace")
    }
    
    shinyApp(ui = ui, server = server)