tellyshia
tellyshia

Reputation: 11

Edited DataTable is not updating in Shiny Module

I would like to embed numericInput and checkBoxInput in a data table. I have an example from Xie Yihui that works perfectly fine. Exactly like what I wanted. But when the code is wrapped in a module, the edited table does not update. I read that this could be due to namespace but I do not know how to fix it.

Here are the Gists:

Gist: edit DataTable in Shiny App

Gist: edit DataTable in Shiny Module

Upvotes: 0

Views: 488

Answers (1)

shosaco
shosaco

Reputation: 6155

There was just one little fix, which is of course hard to see if you're not familiar with Shiny modules. Every Input ID you create in the Module Server function has to be wrapped in ns (as you do in the UI), but this function is hidden in session$ns in the server function. So in line 18, where the inputs are created, their ID had to be adjusted using session$ns. From

inputs[i] = as.character(FUN(paste0(id, i), label = NULL, ...))

to

inputs[i] = as.character(FUN(paste0(session$ns(id), i), label = NULL, ...))

Full code:

library(shiny)
library(DT)


editTableUI <- function(id) {
  ns <- NS(id)
  tagList(
    DT::dataTableOutput(ns('x1')),
    verbatimTextOutput(ns('x2'))
  )
}

editTable <- function(input, output, session) {
  # create a character vector of shiny inputs
  shinyInput = function(FUN, len, id, ...) {
    inputs = character(len)
    for (i in seq_len(len)) {
      inputs[i] = as.character(FUN(paste0(session$ns(id), i), label = NULL, ...))
    }
    inputs
  }



  # obtain the values of inputs
  shinyValue = function(id, len) {
    unlist(lapply(seq_len(len), function(i) {
      value = input[[paste0(id, i)]]
      if (is.null(value)) NA else value
    }))
  }

  # a sample data frame
  res = data.frame(
    v1 = shinyInput(numericInput, 100, 'v1_', value = 0),
    v2 = shinyInput(checkboxInput, 100, 'v2_', value = TRUE),
    v3 = rnorm(100),
    v4 = sample(LETTERS, 100, TRUE),
    stringsAsFactors = FALSE
  )

  # render the table containing shiny inputs
  output$x1 = DT::renderDataTable(
    res, server = FALSE, escape = FALSE, options = list(
      preDrawCallback = JS('function() {
                           Shiny.unbindAll(this.api().table().node()); }'),
      drawCallback = JS('function() {
                        Shiny.bindAll(this.api().table().node()); } ')
      )
      )
  # print the values of inputs
  output$x2 = renderPrint({
    data.frame(v1 = shinyValue('v1_', 100), v2 = shinyValue('v2_', 100))
  })
      }

shinyApp(
  ui = fluidPage(
    editTableUI("test")
  ),

  server = function(input, output) {
    callModule(editTable, "test")
  }
)

Upvotes: 1

Related Questions