0

I wanted to add the functionality of something happening after a table cell was clicked (e.g. open a modal). Because (suppose my dt is has the ID "dt") input$dt_cell_clicked stays the same until I click a new cell, I cannot execute the same event on re-clicking that cell.

I tried working around it resetting input$dt_cell_clicked with javascript manually. This works, but there seems to be an internal updatemarker in DT that noticed I clicked the cell before and does not set the value of input$dt_cell_clicked to the clicked value. Is there a workaround or is this a bug?

Thanks!

Minimal example:

library(shiny)
library(shinyjs)

ui <- fluidPage(
  h2("Last clicked:"),
  verbatimTextOutput("last_clicked"),
  actionButton("reset", "Reset clicked value"),
  h2("Datatable:"),
  DT::dataTableOutput("dt"),
  useShinyjs(),
  extendShinyjs(text = paste0("shinyjs.resetDTClick = function() { Shiny.onInputChange('dt_cell_clicked', null); }"))
)

server <- function(input, output) {

  # the last clicke value
  output$last_clicked <- renderPrint({
    str(input$dt_cell_clicked)
  })

  output$dt <- DT::renderDataTable({
    DT::datatable(head(mtcars, 2))
  })

  observeEvent(input$dt_cell_clicked, {
    validate(need(length(input$dt_cell_clicked) > 0, ''))
    alert("You clicked something!")
  })

  observeEvent(input$reset, {
    js$resetDTClick()
  })
}

shinyApp(ui, server)
shosaco
  • 5,915
  • 1
  • 30
  • 48

2 Answers2

1

After the hint of using a proxy (Gregor de Cillia), I found a workaround using a detour: the last selected cell. This last selected cell is observed and some action is executed upon a new selection. We can reset the selection by using a DT proxy.

library(shiny)

ui <- fluidPage(
  h2("Last clicked:"),
  verbatimTextOutput("last_clicked"),
  actionButton("reset", "Reset clicked value"),
  h2("Datatable:"),
  DT::dataTableOutput("dt")
)

server <- function(input, output) {

  # the last clicked=selected value
  output$last_clicked <- renderPrint({
    str(input$dt_rows_selected)
  })

  output$dt <- DT::renderDataTable({
    DT::datatable(head(mtcars, 2), selection = 'single')
  })

  # do some action after selecting a value
  observeEvent(input$dt_rows_selected, {
    validate(need(!is.null(input$dt_rows_selected), ''))
    print("You clicked something!")
  })

  myProxy = DT::dataTableProxy('dt')

  # reset last selected value using the proxy
  observeEvent(input$reset, {
    DT::selectRows(myProxy, NULL)
  })
}

shinyApp(ui, server)
shosaco
  • 5,915
  • 1
  • 30
  • 48
0

Here is a version that does resets correctly. It uses a reactive value (called last) that gets set to NULL whenever the reset button is pressed and takes the value of input$dt_cell_clicked whenever this value is updated.

I also removed the dependency from shinyjs by using a dataTableProxy together with selectRows

library(shiny)

ui <- fluidPage(
  h2("Last clicked:"),
  verbatimTextOutput("last_clicked"),
  actionButton("reset", "Reset clicked value"),
  h2("Datatable:"),
  DT::dataTableOutput("dt")
)

server <- function(input, output) {

  # the last clicke value
  output$last_clicked <- renderPrint({
    str(last())
  })

  output$dt <- DT::renderDataTable({
    DT::datatable(head(mtcars, 2))
  })

  observeEvent(input$dt_cell_clicked, {
    validate(need(length(input$dt_cell_clicked) > 0, ''))
    print("You clicked something!")
  })

  myProxy = DT::dataTableProxy('dt')
  last = reactiveVal(NULL)

  observe({
    last(input$dt_cell_clicked)
  })

  observeEvent(input$reset, {
    DT::selectRows(myProxy, NULL)
    last(NULL)
    output$dt <- DT::renderDataTable({    # EDIT
      DT::datatable(head(mtcars, 2))      # EDIT
    })                                    # EDIT
  })
}

shinyApp(ui, server)
Gregor de Cillia
  • 7,397
  • 1
  • 26
  • 43
  • thank you, but the only difference in behaviour is the de-selection. `input$dt_last_cell_clicked` is still not updated after the user clicked "reset". The reason is simple: `last` is set to `NULL`, but another click does not update `last`, because `input$dt_cell_clicked` was not reset. – shosaco Jul 13 '17 at 12:02
  • So why don't you use `last` *instead of* `input$dt_last_cell_clicked`? In general, updating inputs is not easy in shiny unless the (`DT`) API supports it. If you need to use this value in javascript, there is another way but as far as I can tell, this was not part of your question. – Gregor de Cillia Jul 14 '17 at 17:16
  • because after `last()` ist set to NULL, and the user clicks on DT, `last` is not updated - and it's obviuos why, since `input$dt_cell_clicked` did not change during the process and so cannot update `last()` – shosaco Jul 14 '17 at 17:38
  • ...and that brings me to the idea to use the last selected cell, and not the last clicked value. Thank you! – shosaco Jul 14 '17 at 17:47
  • you actually don't need the `last()` or re-rendering detour. See my posted answer. – shosaco Jul 14 '17 at 17:54
  • Hmm. I thought i actually tried that and it didn't work. Nice work! – Gregor de Cillia Jul 14 '17 at 17:58
  • Thank you for introducing me to proxies and actually almost solving this :) – shosaco Jul 14 '17 at 18:07