can't make ShinyJS disable a checkbox in Shiny DT - r

I am rendering the full column with checkboxes, each checkbox has its individual id so I can play with the different options and catch the relative states.
in the example below as you can see all the checkboxes will be named chkbx_ the rownumber, e.g. for row1 in the column newvar I'll have a checkbox with id chkbx_1.
now let's say that I want the first checkbox to be disabled, this should be easily achievable as far as I know with ShinyJS (I am doing that for some buttons outside from the DT and it works).
So I am calling the disable function on id chkbx_1, but for some reasons the checkbox is still selectable.
Any idea on how to proceed?
here below the sample code to quickly replicate the issue.
library("DT")
library("shinyjs")
ui <- basicPage(
h2("The mtcars data"),
useShinyjs(),
DT::dataTableOutput("mytable")
)
server <- function(input, output) {
shinyInput <- function(FUN, len, id, ...) {
inputs <- as.character(FUN(paste0(id,'_', len), ...))
inputs
}
mtcarsx <- data.frame(mtcars)
for(myrow in 1:nrow(mtcarsx)){
mtcarsx[myrow,"newvar"]=toString(shinyInput(checkboxInput,toString(myrow),"chkbx",label="",value=FALSE,width=NULL))
}
shinyjs::disable("chkbx_1")
output$mytable = DT::renderDataTable({
DT::datatable(mtcarsx,
escape = FALSE,
selection = 'none',
rownames = TRUE,
extensions = c('FixedColumns'),
options = list(searching = FALSE,
ordering = FALSE,
autoWidth = TRUE,
scrollX = TRUE,
FixedColumns = list(leftColumns = c(2))
))
})
}
shinyApp(ui, server)

Try this
library("DT")
library("shinyjs")
ui <- basicPage(
h2("The mtcars data"),
useShinyjs(),
DT::dataTableOutput("mytable")
)
server <- function(input, output) {
shinyInput <- function(FUN, len, id, ...) {
inputs <- as.character(FUN(paste0(id,'_', len), ...))
inputs
}
mtcarsx <- data.frame(mtcars)
for(myrow in 1:nrow(mtcarsx)){
mtcarsx[myrow,"newvar"]=toString(shinyInput(checkboxInput,toString(myrow),"chkbx",label="",value=FALSE,width=NULL))
}
observe({
shinyjs::disable("chkbx_1")
print(input$chkbx_2)
})
output$mytable = DT::renderDataTable({
DT::datatable(mtcarsx,
escape = FALSE,
selection = 'none',
rownames = TRUE,
extensions = c('FixedColumns'),
options = list(searching = FALSE,
ordering = FALSE,
autoWidth = TRUE,
scrollX = TRUE,
FixedColumns = list(leftColumns = c(2)),
preDrawCallback = JS('function() { Shiny.unbindAll(this.api().table().node()); }'),
drawCallback = JS('function() { Shiny.bindAll(this.api().table().node()); } ')
))
})
}
shinyApp(ui, server)

Related

Specifying scroll options in DT::datatable affects reactivity of inputs in the table header if using renderUI

I discovered a peculiar behavior of the datatable in Shiny R.
I am adding active inputs to the header in the datatable using approach similar to the one posted in this thread.
For some reason there is a difference in behavior for cases of datatable rendered directly from within ui and datatable rendered from renderUI().
I found that both ui and renderUI ways work fine unless scrollX (or scrollY) options added to the list of options in the datatable. If scroll option is present the inputs in the datatable header in the case with renderUI are inactive. ui approach works with scroll options added.
In my case I need to use renderUI, so need your help with resolving this.
The code that demonstrates the behavior is below:
library(shiny)
library(DT)
ui <- fluidPage(
verbatimTextOutput("text"),
br(),
splitLayout(cellWidths = 300,
tagList(
h3("Button from renderUI"),
actionButton("button", label = "Press Me"),
h3("Table without renderUI"),
DT::datatable(
data = data.frame(a = letters[1:3]),
colnames = c(
as.character(
actionButton("button_a", label = "Button A")
)
),
rownames = FALSE,
escape = FALSE,
options = list(ordering = FALSE, scrollY = TRUE,
preDrawCallback = JS('function() { Shiny.unbindAll(this.api().table().node()); }'),
drawCallback = JS('function() { Shiny.bindAll(this.api().table().node()); } '))
)
),
br(),
tagList(
h3("Button from renderUI"),
uiOutput("custom"),
h3("Table from renderUI"),
uiOutput("dt"),
br()
)
)
)
server <- function(input, output, session) {
output$custom <- renderUI({
actionButton("button_render", label = "Press Me")
})
output$dt <- renderUI({
DT::datatable(
data = data.frame(a = letters[1:3]),
colnames = c(
as.character(
actionButton("button_b", label = "Button B")
)
),
rownames = FALSE,
escape = FALSE,
options = list(ordering = FALSE, scrollY = TRUE,
preDrawCallback = JS('function() { Shiny.unbindAll(this.api().table().node()); }'),
drawCallback = JS('function() { Shiny.bindAll(this.api().table().node()); } '))
)
})
observeEvent(input$button_a, {
output$text <- renderText(paste("button_a pressed:", input$button_a))
})
observeEvent(input$button_b, {
output$text <- renderText(paste("button_b pressed:", input$button_b))
})
observeEvent(input$button_render, {
output$text <- renderText(paste("button_render pressed:", input$button_render))
})
observeEvent(input$button, {
output$text <- renderText(paste("button pressed:", input$button))
})
}
shinyApp(ui, server)
In the UI you have to remove the JavaScript; it is useless and it throws an error.
In the server you have to bind/unbind table().header():
options = list(
ordering = FALSE,
scrollY = TRUE,
preDrawCallback =
JS('function() { Shiny.unbindAll(this.api().table().header()); }'),
drawCallback =
JS('function() { Shiny.bindAll(this.api().table().header()); }')
)
It's more idiomatic to use renderDT/DTOutput.

How to observeEvent for selectInput present in each row in a column

I would like to obtain the row number and choice selected each time an input is changed in one of the selectInput. The following is a test code. So in short if I change the species in row three, using observeEvent I would like the output to tell me what row was it in and what was picked.
Is there a way of doing this.
library(shiny)
library(DT)
ui <- fluidPage(
DT::dataTableOutput('foo'),
textOutput("text")
)
server <- function(input, output, session) {
data <- head(iris, 5)
for (i in 1:nrow(data)) {
data$species_selector[i] <- as.character(selectInput(paste0("change", i), label = paste0("change", i), choices = unique(iris$Species), width = "100px"))
}
output$foo = DT::renderDataTable(
data, escape = FALSE, selection = 'none', server = FALSE,
options = list(dom = 't', paging = FALSE, ordering = FALSE))
observeEvent$...
}
shinyApp(ui, server)
First, you have to use these options preDrawCallback and drawCallback, otherwise Shiny is not aware of the selectors:
output[["foo"]] <- renderDT(
data, escape = FALSE, selection = 'none', server = FALSE,
options = list(
dom = 't',
paging = FALSE,
ordering = FALSE,
preDrawCallback = JS(
"function() { Shiny.unbindAll(this.api().table().node()); }"
),
drawCallback = JS(
"function() { Shiny.bindAll(this.api().table().node()); }"
)
)
)
Now, you can use two reactive values to store the row and the species:
row <- reactiveVal()
species <- reactiveVal()
And then, define an observer for each row:
lapply(1:nrow(data), function(i){
selector <- paste0("change", i)
observeEvent(input[[selector]], {
row(i)
species(input[[selector]])
})
})
Full app:
library(shiny)
library(DT)
ui <- fluidPage(
br(),
DTOutput('foo'),
br(),
wellPanel(
textOutput("text")
)
)
server <- function(input, output, session) {
data <- head(iris, 5)
data$species_selector <- vapply(1:nrow(data), function(i){
as.character(selectInput(
paste0("change", i),
label = paste0("change", i),
choices = unique(iris$Species),
width = "100px"
))
}, character(1))
output[["foo"]] <- renderDT(
data, escape = FALSE, selection = 'none', server = FALSE,
options = list(
dom = 't',
paging = FALSE,
ordering = FALSE,
preDrawCallback = JS(
"function() { Shiny.unbindAll(this.api().table().node()); }"
),
drawCallback = JS(
"function() { Shiny.bindAll(this.api().table().node()); }"
)
)
)
row <- reactiveVal()
species <- reactiveVal()
lapply(1:nrow(data), function(i){
selector <- paste0("change", i)
observeEvent(input[[selector]], {
row(i)
species(input[[selector]])
})
})
output[["text"]] <- renderText({
sprintf("Row %d --- Species %s", row(), species())
})
}
shinyApp(ui, server)

Add a scroll bar for datatable in shiny when using wellPanel

I'm trying to add an X scroll bar for datatable when it's wrapped around a fixedPanel. See the following example:
library(shiny)
library(shinydashboard)
library(DT)
ui <- function(request) {
dashboardPage(
skin = "black",
dashboardHeader(),
dashboardSidebar(disable = TRUE),
dashboardBody(
fluidRow(
uiOutput("table")
),
fluidRow(
DT::dataTableOutput("data2")
)
)
)
}
server <- function(input, output, session) {
output[["data"]] <-
DT::renderDataTable({
cbind(iris, iris, iris, iris, iris)[1, ]
},
selection = "none",
options = list(
searching = FALSE,
lengthChange = FALSE,
paginate = FALSE,
scroller = TRUE,
scrollX = TRUE
))
output[["table"]] <-
renderUI({
fixedPanel(
wellPanel(div(style = 'overflow-x: scroll', DT::dataTableOutput("data"))),
style = "z-index: 10;"
)
})
output[["data2"]] <-
DT::renderDataTable({
cbind(iris, iris, iris, iris, iris)
},
options = list(
scroller = TRUE,
scrollX = TRUE,
pageLength = 25
))
}
shinyApp(ui, server)
In the opposite I could use the shiny box object and the scrolling works but then I don't have this datatable on top of other ui (style = "z-index: 10;") that I need in my app:
output[["table"]] <-
renderUI({
box(div(style = 'overflow-x: scroll', DT::dataTableOutput("data")),
width = 12,
style = "z-index: 10;") # this line doesn't work
})
Is it possible to combine the two? I'd rather use fixedPanel than box, but I need both components from datatable: scrolling and being on top of other ui output.
See this post: https://stackoverflow.com/a/55574864/3439739
renderUI({
fixedPanel(
wellPanel(div(style = 'overflow-x: scroll', DT::dataTableOutput("data"))),
style = "z-index: 10; left:0; right:0; overflow-y:hidden; overflow-xy:auto"
)
})
seems to do the job.

Why do R/Shiny inputs in datatable not work correctly after updating datatable?

I'm trying to create a datatable with Shiny input elements (checkboxInput or textInput). This works well until I update the datatable. If I add more rows with more input elements, only the new elements work. I thought the table would be recreated every time I update it and the ids would be associated with the new input elements. The code example below illustrates the problem. It creates a table with one row first. If I then create a table with two rows using the dropdown on the left, I can only read the values of the second row in the output table. Any change to the inputs of the first row has no impact on the ouput table.
library(DT)
library(shiny)
server <- function(input, output) {
updateTable <- reactive({
num <- as.integer(input$num)
df <- data.frame(check = unlist(lapply(1:num, function(i) as.character(checkboxInput(paste0("check_", i), label = paste0("check", i), value = 0)))),
text = unlist(lapply(1:num, function(i) as.character(textInput(paste0("text_",i), label = paste0("text", i), value = "")))))
})
output$input_ui <- DT::renderDataTable(
updateTable(),
server = FALSE, escape = FALSE, selection = 'none',
options = list(
dom = 't', paging = FALSE, ordering = FALSE,lengthChange = TRUE,
preDrawCallback = JS('function() { Shiny.unbindAll(this.api().table().node()); }'),
drawCallback = JS('function() { Shiny.bindAll(this.api().table().node()); } ')
)
)
output$table <- renderTable({
num <- as.integer(input$num)
data.frame(lapply(1:num, function(i) {
paste(input[[paste0("check_", i)]], input[[paste0("text_",i)]], sep = " : ")
}))
})
}
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("num", "select number of inputs", choices = seq(1,10,1))
),
mainPanel(
DT::dataTableOutput("input_ui"),
tableOutput("table")
)
)
)
shinyApp(ui = ui, server = server)
A possible solution is provided here:
https://groups.google.com/d/msg/shiny-discuss/ZUMBGGl1sss/7sdRQecLBAAJ
As far as I understand, it allows to "force" a complete unbind of all checkbox/textinpts before redrawing the table thanks to the use of:
session$sendCustomMessage('unbind-DT', 'input_ui')
. I do not pretend to really understsand it, but apparently it works. See below for a possible implementation.
library(shiny)
library(DT)
server <- function(input, output,session) {
updateTable <- reactive({
num <- as.integer(input$num)
session$sendCustomMessage('unbind-DT', 'input_ui')
df <- data.frame(
check = unlist(lapply(1:num, function(i) as.character(checkboxInput(paste0("check_", i), label = paste0("check", i), value = 0)))),
text = unlist(lapply(1:num, function(i) as.character(textInput(paste0("text_",i), label = paste0("text", i), value = "")))))
tbl <- DT::datatable(df, escape = FALSE,
selection = "none",
options = list(
dom = 't', paging = FALSE, ordering = FALSE,lengthChange = TRUE,
preDrawCallback = JS('function() { Shiny.unbindAll(this.api().table().node()); }'),
drawCallback = JS('function() { Shiny.bindAll(this.api().table().node()); } ')
))
})
output$input_ui <- DT::renderDataTable(
updateTable(),
server = FALSE
)
output$table <- renderTable({
num <- as.integer(input$num)
data.frame(lapply(1:num, function(i) {
paste(input[[paste0("check_", i)]], input[[paste0("text_",i)]], sep = " : ")
}))
})
}
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("num", "select number of inputs", choices = seq(1,10,1))
),
mainPanel(
DT::dataTableOutput("input_ui"),
tags$script(HTML(
"Shiny.addCustomMessageHandler('unbind-DT', function(id) {
Shiny.unbindAll($('#'+id).find('table').DataTable().table().node());
})")),
tableOutput("table")
)
)
)
shinyApp(ui = ui, server = server)
HTH!

DT rows_selected doesn't work with FixedColumns extension

When using the FixedColumns extension in DT, the rows_selected doesn't register any selections in Sepel.Length
Please see the below example...
Any suggestions on how to get around this would be appreciated.
library(DT)
library(shiny)
ui=shinyUI(
fluidPage(
DT::dataTableOutput("x3")
)
)
server=shinyServer(function(input, output) {
output$x3 = DT::renderDataTable(
DT:::datatable(
iris, rownames=FALSE,
extensions = c('FixedColumns'),
options = list(
fixedColumns = list(leftColumns = 1),
scrollX = TRUE)
))
observe({
s = input$x3_rows_selected
print(s)
})
})
shinyApp(ui=ui,server=server)

Resources