I have a data coming from a server. Now I want to add a free text column ( editable) to add comments to my R shiny application. Once that is done , I want to save it in SQLLite and bring it back once it is refreshed. Please help me with the pointers.
library(shiny)
library(ggplot2) # for the diamonds dataset
ui <- fluidPage(
title = "Examples of DataTables",
sidebarLayout(
sidebarPanel(
conditionalPanel(
'input.dataset === "diamonds"'
)
),
mainPanel(
tabsetPanel(
id = 'dataset',
tabPanel("diamonds", DT::dataTableOutput("mytable1"))
)
)
)
)
library(DT)
server <- function(input, output) {
# choose columns to display
diamonds2 = diamonds[sample(nrow(diamonds), 1000), ]
diamonds2$test <- ifelse(diamonds2$x > diamonds2$y,TRUE,FALSE)
output$mytable1 <- DT::renderDataTable({
DT::datatable(diamonds2[, drop = FALSE],extensions = 'FixedColumns',options = list(
dom = 't',
scrollX = TRUE,
fixedColumns = list(leftColumns =10)
)) %>%
formatStyle(
'x', 'test',
backgroundColor = styleEqual(c(TRUE, FALSE), c('gray', 'yellow'))
)
})
}
Please guide how can I add free text in the end of the table and save it.
Thanks in advance.
Regards,
R
Here is a solution based on DTs editable option. (See this for more information)
Each time the user edits a cell in the "comment" column it is saved to a sqlite database and loaded again after restarting the app:
library(shiny)
library(DT)
library(ggplot2) # diamonds dataset
library(RSQLite)
library(DBI)
# choose columns to display
diamonds2 = diamonds[sample(nrow(diamonds), 1000),]
diamonds2$test <- ifelse(diamonds2$x > diamonds2$y, TRUE, FALSE)
diamonds2$id <- seq_len(nrow(diamonds2))
diamonds2$comment <- NA_character_
con <- dbConnect(RSQLite::SQLite(), "diamonds.db")
if(!"diamonds" %in% dbListTables(con)){
dbWriteTable(con, "diamonds", diamonds2)
}
ui <- fluidPage(title = "Examples of DataTables",
sidebarLayout(sidebarPanel(
conditionalPanel('input.dataset === "diamonds"')
),
mainPanel(tabsetPanel(
id = 'dataset',
tabPanel("diamonds", DT::dataTableOutput("mytable1"))
))))
server <- function(input, output, session) {
# use sqlInterpolate() for production app
# https://shiny.rstudio.com/articles/sql-injections.html
dbDiamonds <- dbGetQuery(con, "SELECT * FROM diamonds;")
output$mytable1 <- DT::renderDataTable({
DT::datatable(
dbDiamonds,
# extensions = 'FixedColumns',
options = list(
dom = 't',
scrollX = TRUE
# , fixedColumns = list(leftColumns = 10)
),
editable = TRUE,
# editable = list(target = "column", disable = list(columns = which(names(diamonds2) %in% setdiff(names(diamonds2), "comment"))))
) %>% formatStyle('x', 'test', backgroundColor = styleEqual(c(TRUE, FALSE), c('gray', 'yellow')))
})
observeEvent(input$mytable1_cell_edit, {
if(input$mytable1_cell_edit$col == which(names(dbDiamonds) == "comment")){
dbExecute(con, sprintf("UPDATE diamonds SET comment = '%s' WHERE id = %s", input$mytable1_cell_edit$value, input$mytable1_cell_edit$row))
}
})
}
shinyApp(ui, server, onStart = function() {
onStop(function() {
dbDisconnect(con) # close connection on app stop
})
})
Initially I wanted to disable editing for all columns except "comment", however, it seems I've found a bug.
The following example adds a <input type="text"> element to each row of the table, where you can add your free text. A simple JavaScript event listener reacts on changes to the text boxes and stores them in the Shiny variable free_text which you can then process on the shiny side according to your needs (in this toy example it is simply output to a verbatimTextOutput).
As for the storing: I would add a save button, which reads input$free_text and saves it back to the data base. To display the text then again in the text boxes is as easy as adding the value in the mutate statement like this mutate(free_text = sprintf("<input type=\"text\" class = \"free-text\" value = \"%s\" />", free_text_field_name))
library(shiny)
library(DT)
library(dplyr)
ui <- fluidPage(
tags$head(
tags$script(
HTML(
"$(function() {
// input event fires for every change, consider maybe a debounce
// or the 'change' event (then it is only triggered if the text box
// loses focus)
$('#tab').on('input', function() {
const inputs = $(this).find('.free-text').map(function() {
return this.value;
})
Shiny.setInputValue('free_text', inputs.get());
})
})
"
)
)
),
fluidRow(
verbatimTextOutput("out")
),
fluidRow(
dataTableOutput("tab")
)
)
server <- function(input, output, session) {
output$tab <- renderDataTable({
my_dat <- mtcars %>%
mutate(free_text =
sprintf("<input type=\"text\" class = \"free-text\" value = \"\" />"))
datatable(my_dat, escape = FALSE,
options = list(dom = "t", pageLength = nrow(mtcars)))
})
output$out <- renderPrint(input$free_text)
}
shinyApp(ui, server)
You may want to have a look at the handsontable package, which allows editing of (columns of) datatable outputs. In your case, you can create a character column and allow editing through the handsontable.
On the topic of persisting data: you table would need either a separate column with comments, or a separate table that maps observations to comment, which is joined. The best solution depends on the volume of comments you expect: if you expect comment to appears sporadically, a separate table may be the best solution. If you expect comments for nearly every row, direct integration into the table may be more favourable. It then becomes a matter of writing to and loading from an SQL database based on user events.
Related
I have the following app which I created with some help from stack overflow
I want to be able to pick the practice for each doctor, then create a table based on the user input, then be able to export that table.
The app needs to adjust for a variable amount of doctors each time (the real app pulls from a dynamic database with new docs being added daily), hence the renderUI with conditional panels
I am having trouble passing selections from the practice into a table which I can render and export.
Any help much appreciated.
Here is my reprex
library(tidyverse)
library(shiny)
find_docs <- dplyr::tibble(record = c("joe", "mary", "dan", "suzie"))
locs_locs <- dplyr::tibble(record = c("practice1", "practice2", "practice3"))
mytable <- dplyr::tibble(
doc = find_docs$record,
location = rep("", length(find_docs$record))
)
ui <- fluidPage(
#numericInput("num_selected", label = "Fields to Display", value = 0, min = 0, max = 10, step = 1),
uiOutput("condPanels"),
tableOutput(outputId = "mydt")
)
server<-function(input,output,session){
output$condPanels <- renderUI({
# if selected value = 0 dont create a condPanel,...
# if(!input$num_selected) return(NULL)
tagList(
lapply(head(find_docs$record), function(nr){
conditionalPanel(
condition = paste0("Find DOC", nr),
fluidRow(
column(3,
tags$br(),
nr
),
column(3, selectInput(paste0("DOC", nr), "pick loc",
choices = locs_locs))
)
)
})
)
})
output$mydt <- renderTable({
#somehow i need to use mytable here
z <- data.frame( g = rep(input$find_docs$record[1], length(find_docs$record)))
z
# i want to render a table of find_docs in one column, and the selections in a second column)
# then i want to be able to export the table as csv
})
}
shinyApp(ui=ui, server=server)
I think I was able to capture your two needs. First, I took the inputs from each of the select inputs to create the table. I used lapply to pass each of the doctors to the input name. Then I combined this with the doctor list to create a data frame and a table.
I used the package DT for the second part of your request, to be able to download. DT has an extension which has a really easy way to download files in different ways. Hopefully this helps, good luck!
library(tidyverse)
library(shiny)
library(DT) #Added DT to download the table easily
find_docs <- dplyr::tibble(record = c("joe", "mary", "dan", "suzie"))
locs_locs <- dplyr::tibble(record = c("practice1", "practice2", "practice3"))
mytable <- dplyr::tibble(
doc = find_docs$record,
location = rep("", length(find_docs$record))
)
ui <- fluidPage(
uiOutput("condPanels"),
DTOutput(outputId = "mydt")
)
server<-function(input,output,session){
output$condPanels <- renderUI({
tagList(
lapply(head(find_docs$record), function(nr){
conditionalPanel(
condition = paste0("Find DOC", nr),
fluidRow(
column(3,
tags$br(),
nr
),
column(3, selectInput(paste0("DOC", nr), "pick loc",
choices = locs_locs))
)
)
})
)
})
output$mydt <- renderDT({
#An error will occur without this as it's trying to pull before these inputs are rendered
req(input[[paste0("DOC",find_docs$record[1])]])
z<-lapply(find_docs$record, function(x){
input[[paste0("DOC",x)]]
}) #Grab each of the inputs
z2 <- data.frame("DOC" = find_docs$record, "LOC" = unlist(z)) #Combine into a data frame
z2
}, extensions = "Buttons", #Using the extension addon of DT to have options to download the table
options = list(dom = 'Bfrtip',
buttons = c('csv')) #Download types
)
}
shinyApp(ui=ui, server=server)
If you didn't want to use DT, you could also put the table into a reactiveValue, and then download it using the download button. Both of these download options are visible on this other page I just realized: Shiny R - download the result of a table
I want to creat an shiny app where users have to edit datatable.
There is the code contains reproductible exemple:
library(shiny)
library(dplyr)
library(DT)
line<-c(1,1,1,1,1)
op<-c(155,155,155,156,156)
batch<-c(1,2,3,1,2)
voile<-c(1,NA,NA,NA,NA)
depot<-c(2,NA,2,NA,NA)
boe<-data.frame(line,op,batch)
ui <- fluidPage(
# Application title
titlePanel("test dust"),
actionButton("refresh", label = "refresh"),
DT::dataTableOutput("mytable"),
actionButton("save", label = "save"),
)
# Define server logic required to draw a histogram
server <- function(input, output) {
DTdust<- eventReactive(input$refresh, {
DTdust <-data.frame(line,op,batch,voile,depot)
})
merged<-reactive({
merged<-merge(boe,DTdust(),all.x = TRUE)
})
mergedfiltred<-reactive({
mergedfiltred<- filter(merged(),is.na(voile)|is.na(depot) )
})
output$mytable = DT::renderDataTable( mergedfiltred(),editable = list(target = 'cell',
disable = list(columns = c(1:3))),selection = 'none'
)
}
# Run the application
shinyApp(ui = ui, server = server)
I wish this works like this —>
When user clic on refresh button. Dtdust.csv (here simulated) is read , then it merged with boe.csv (simulated too) an filter to get only rows without resulta for voile and depot col.
And display this merged filtred ino editable datatable .
This part works.
After i want to extract the data from edited datatable to make some processing on it (extract rows completed, rbind it on dtdust and save as dtdust.csv. But that’s ok i think.)
I’ m in trouble to extract edited datatable.
I see some exemple to do it with classic dataframe but it not work with reactive one.
I’m beeginner so if you can comment a lot your answers i can learn how to and not just ctrl+c ctrl+v your code :)
Thanks
You need to define a reactiveValues data frame. Then you need to update it via observeEvent whenever any cell is modified via mytable_cell_edit. The updated dataframe is now available in the server side, and part of it is now printed in the second table. You can use DF1$data for further analysis or subsetting. Full updated code is below.
library(shiny)
library(dplyr)
library(DT)
line<-c(1,1,1,1,1)
op<-c(155,155,155,156,156)
batch<-c(1,2,3,1,2)
voile<-c(1,NA,NA,NA,NA)
depot<-c(2,NA,2,NA,NA)
boe<-data.frame(line,op,batch)
ui <- fluidPage(
# Application title
titlePanel("test dust"),
actionButton("refresh", label = "refresh"),
DTOutput("mytable"), DTOutput("tb2"),
actionButton("save", label = "save"),
)
# Define server logic required to draw a histogram
server <- function(input, output) {
DF1 <- reactiveValues(data=NULL)
DTdust<- eventReactive(input$refresh, {
req(input$refresh)
DTdust <-data.frame(line,op,batch,voile,depot)
})
merged<-reactive({
req(DTdust())
merged<-merge(boe,DTdust(),all.x = TRUE)
})
mergedfiltred<-reactive({
mergedfiltred <- filter(merged(),is.na(voile)|is.na(depot) )
DF1$data <- mergedfiltred
mergedfiltred
})
output$mytable = renderDT(
mergedfiltred(),
editable = list(target = 'cell', disable = list(columns = c(1:3))), selection = 'none'
)
observeEvent(input$mytable_cell_edit, {
info = input$mytable_cell_edit
str(info)
i = info$row
j = info$col
v = info$value
DF1$data[i, j] <<- DT::coerceValue(v, DF1$data[i, j])
})
output$tb2 <- renderDT({
df2 <- DF1$data[,2:5]
plen <- nrow(df2)
datatable(df2, class = 'cell-border stripe',
options = list(dom = 't', pageLength = plen, initComplete = JS(
"function(settings, json) {",
"$(this.api().table().header()).css({'background-color': '#000', 'color': '#fff'});",
"}")))
})
}
# Run the application
shinyApp(ui = ui, server = server)
Hi thanks for your solution #YBS.
I finaly find a solution by myself half an hour after asking here... (i previously turning arround hours and hours).
There is what i do :
output$x2 = DT::renderDataTable({
req(dat$x2)
DT::datatable(dat$x2)
})
dat <- reactiveValues()
# update edited data
observeEvent(input$mytable_cell_edit, {
data_table <- dat$x2
data_table[input$mytable_cell_edit$row, input$mytable_cell_edit$col] <- as.numeric(input$mytable_cell_edit$value)
dat$x2 <- data_table
})
Have a good day
I have been trying to create ActionButtons to allow a user to 'Select all rows in view' in a reactive, filtering datatable.
Currently the button does this using tableid_rows_current; however, I also want to add in a table proxy so that it doesn't reset to the first page of results if you're on another page, but I can't figure out the syntax after much googling (see attempts commented out in code). Also if you manually select some rows, it no longer works.
Another ActionButton that allows a user to 'add all rows in view to selection'. That is to add all current rows in view to your previous selection. This one I'm not even sure where to start, so any ideas are appreciated.
(Not included here, but I do have functioning 'clear selection' and 'clear filter' buttons already, if anyone is interested)
Minimum reproducible code below. The app is meant to display the images for the selected rows, but not a big deal here that you won't have actual images displaying.
library(DT)
library(shiny)
dat <- data.frame(
type = c("car", "truck", "scooter", "bike"),
frontimage = c("carf.jpg", "truckf.jpg", "scooterf.jpg", "bikef")
)
# ----UI----
ui <- fluidPage(
titlePanel("Buttons 'select all' and 'add to select'"),
mainPanel(
DTOutput("table"),
actionButton("select_all_current", "Select All Rows in View"),
actionButton("add_to_selection", "Add All Rows in View to Selection"),
uiOutput("img1")
)
)
# ----Server----
server = function(input, output, session){
# Action button to select all rows in current view
var <- reactiveValues()
tableProxy <- dataTableProxy('table')
observeEvent(input$select_all_current, {
print("select_all_current")
# tableProxy %>% selectRows(1:nrow(input$table_rows_current))
# var$selected <- tableProxy %>% input$table_rows_current
tableProxy <- #I want the table proxy to be whatever the current selection and filters are and the current page view to stay the same after selecting
var$selected <- input$table_rows_current
})
# Action button to add all rows in current view to previous selection
observeEvent(input$add_to_selection, {
print("select_all_current")
})
# Data table with filtering
output$table = DT::renderDT({
datatable(dat, filter = list(position = "top", clear = FALSE),
selection = list(target = 'row', selected = var$selected),
options = list(
autowidth = TRUE,
pageLength = 2,
lengthMenu = c(2, 4)
))
})
# Reactive call that only renders images for selected rows
df <- reactive({
dat[input[["table_rows_selected"]], ]
})
# Front image output
output$img1 = renderUI({
imgfr <- lapply(df()$frontimage, function(file){
tags$div(
tags$img(src=file, width="100%", height="100%")
)
})
do.call(tagList, imgfr)
})
}
# ----APP----
# Run the application
shinyApp(ui, server)
Does this do what you're looking for?
library(DT)
library(shiny)
dat <- data.frame(
type = c("car", "truck", "scooter", "bike"),
frontimage = c("carf.jpg", "truckf.jpg", "scooterf.jpg", "bikef")
)
# ----UI----
ui <- fluidPage(
titlePanel("Buttons 'select all' and 'add to select'"),
mainPanel(
DTOutput("table"),
actionButton("select_all_current", "Select All Rows in View"),
actionButton("add_to_selection", "Add All Rows in View to Selection"),
uiOutput("img1")
)
)
# ----Server----
server = function(input, output, session){
# Action button to select all rows in current view
var <- reactiveValues()
tableProxy <- dataTableProxy('table')
observeEvent(input$select_all_current, {
print("select_all_current")
# tableProxy %>% selectRows(1:nrow(input$table_rows_current))
# var$selected <- tableProxy %>% input$table_rows_current
# tableProxy <- #I want the table proxy to be whatever the current selection and filters are and the current page view to stay the same after selecting
# var$selected <- input$table_rows_current
selectRows(proxy = tableProxy,
selected = input$table_rows_current)
})
# Action button to add all rows in current view to previous selection
observeEvent(input$add_to_selection, {
print("select_all_current")
selectRows(proxy = tableProxy,
selected = c(input$table_rows_selected, input$table_rows_current))
})
# Data table with filtering
output$table = DT::renderDT({
datatable(dat, filter = list(position = "top", clear = FALSE),
selection = list(target = 'row'),#, selected = var$selected),
options = list(
autowidth = TRUE,
pageLength = 2,
lengthMenu = c(2, 4)
))
})
# Reactive call that only renders images for selected rows
df <- reactive({
dat[input[["table_rows_selected"]], ]
})
# Front image output
output$img1 = renderUI({
imgfr <- lapply(df()$frontimage, function(file){
tags$div(
tags$img(src=file, width="100%", height="100%")
)
})
do.call(tagList, imgfr)
})
}
# ----APP----
# Run the application
shinyApp(ui, server)
I have a Shiny app with a datatable. I would like to implement a button at the top of this datatable (but below its title) so that, when I click on it, the LaTeX code necessary to build this table is copied to clipboard.
Basically, this button would work the same way that the "copy" or "csv" buttons (see here part 2) but with LaTeX code.
Here's a reproducible example :
library(DT)
library(shiny)
library(shinydashboard)
library(data.table)
library(stargazer)
library(clipr)
ui <- dashboardPage(
dashboardHeader(title = "test with mtcars", titleWidth = 1000),
dashboardSidebar(
selectizeInput("var.cor", label = "Correlation",
choices = names(mtcars),
selected = c("mpg", "cyl"),
multiple = TRUE)
),
dashboardBody(
tabsetPanel(
tabPanel("test with mtcars",
br(),
box(dataTableOutput("cor"),
width = NULL),
actionButton("copy.latex", label = "Copy to LaTeX")
)
)
)
)
server <- function(input, output) {
var.selected <- reactive({
out <- input$var.cor
out
})
user.selection <- reactive({
mtcars <- mtcars[, var.selected()]
})
output$cor <- renderDataTable({
dtable <- user.selection()
tmp <- datatable(cor(dtable),
extensions = 'Buttons',
options = list(
dom = 'Bfrtip',
buttons = list(
"copy",
list(
extend = "collection",
text = 'test',
action = DT::JS("function ( e, dt, node, config ) {
Shiny.setInputValue('test', true, {priority: 'event'});
}")
)
)
)
)
observeEvent(input$test, {
write_clip(stargazer(tmp),
object_type = "auto")
})
tmp
})
observeEvent(input$copy.latex, {
write_clip(stargazer(input$cor),
object_type = "character")
})
}
shinyApp(ui, server)
I tested two things in this code :
firstly, I inspired from here. This is the code of observeEvent nested in renderDataTable. However, either the text in the clipboard is % Error: Unrecognized object type, either I have an error : Error in : Clipboard on X11 requires that the DISPLAY envvar be configured.
secondly, I created a button outside the datatable but it doesn't work because I have Error in : $ operator is invalid for atomic vectors
Does somebody know how to do it ?
To copy the dataframe to clipboard in server:
library(shiny)
library(shinyjs)
library(DT)
table <- iris[1:10,]
ui <- fluidPage(
useShinyjs(),
actionButton("latex","Copy Latex to Clipboard"),
DT::dataTableOutput("table")
)
server <- function(input, output, session) {
output$table <- DT::renderDT(table)
observeEvent(input$latex,{
writeClipboard(paste0(capture.output(xtable(table))[-c(1:2)],collapse = "\n"))
shinyjs::alert("table copied to latex")
})
}
shinyApp(ui, server)
I won't recommend you to do it using DT's button. In order to do it using DT, there are at least 3 steps:
read entire table in the UI of datatable by writing Javascript in action, use Shiny.setInputValue to send the value from UI to server.
use R to parse the list(json) into data frame.
convert the data frame to latex string.
It's much easier to just do the conversion using the source data for datatable.
I would like to have a working example similar to this:
https://demo.shinyapps.io/029-row-selection/
I tried the example in my Shiny server running Shiny Server v1.1.0.10000, packageVersion: 0.10.0 and Node.js v0.10.21, but it is not working even if I load the js and css files from the website. It simply does not select rows from the table:
# ui.R
library(shiny)
shinyUI(fluidPage(
title = 'Row selection in DataTables',
tagList(
singleton(tags$head(tags$script(src='//cdn.datatables.net/1.10.2/js/jquery.dataTables.js',type='text/javascript'))),
singleton(tags$head(tags$script(src='//cdn.datatables.net/1.10.2/css/jquery.dataTables.min.css',type='text/css')))
),
sidebarLayout(
sidebarPanel(textOutput('rows_out')),
mainPanel(dataTableOutput('tbl')),
position = 'right'
)
))
# server.R
library(shiny)
shinyServer(function(input, output) {
output$tbl <- renderDataTable(
mtcars,
options = list(pageLength = 10),
callback = "function(table) {
table.on('click.dt', 'tr', function() {
$(this).toggleClass('selected');
Shiny.onInputChange('rows',
table.rows('.selected').indexes().toArray());
});
}"
)
output$rows_out <- renderText({
paste(c('You selected these rows on the page:', input$rows),
collapse = ' ')
})
})
I then tried to do this from a different example that was using radio buttons to re-sort the rows.
In my modified example, I want to produce a list of ids from the selected checkbox buttons of the dataTables table shown in the webpage. E.g., selecting some rows from the first 5, I want my textbox to be: 1,3,4 corresponding to the mymtcars$id column I added to mtcars. I then plan to link an action to the values of the textbox.
I have it almost there in this example, but checking the boxes does not update the list in the textbox. Differently to the example shinyapp, I would like my checkboxes to keep the selection status if the table is resorted. This may be the tricky part, and I am not sure how to do it. I would also like to add a "Select/Unselect all" textbox on the top left corner of the table, that selects/unselects all boxes in the table. Any ideas?
# server.R
library(shiny)
mymtcars = mtcars
mymtcars$id = 1:nrow(mtcars)
shinyServer(function(input, output, session) {
rowSelect <- reactive({
if (is.null(input[["row"]])) {
paste(sort(unique(rep(0,nrow(mymtcars)))),sep=',')
} else {
paste(sort(unique(input[["row"]])),sep=',')
}
})
observe({
updateTextInput(session, "collection_txt",
value = rowSelect()
,label = "Foo:"
)
})
# sorted columns are colored now because CSS are attached to them
output$mytable = renderDataTable({
addCheckboxButtons <- paste0('<input type="checkbox" name="row', mymtcars$id, '" value="', mymtcars$id, '">',"")
#Display table with checkbox buttons
cbind(Pick=addCheckboxButtons, mymtcars[, input$show_vars, drop=FALSE])
}, options = list(bSortClasses = TRUE, aLengthMenu = c(5, 25, 50), iDisplayLength = 25))
})
# ui.R
library(shiny)
mymtcars = mtcars
mymtcars$id = 1:nrow(mtcars)
shinyUI(pageWithSidebar(
headerPanel('Examples of DataTables'),
sidebarPanel(
checkboxGroupInput('show_vars', 'Columns to show:', names(mymtcars),
selected = names(mymtcars))
),
mainPanel(
dataTableOutput("mytable")
,textInput("collection_txt",label="Foo")
)
)
)
For the first problem you need the dev version of shiny and htmltools >= 0.2.6 installed:
# devtools::install_github("rstudio/htmltools")
# devtools::install_github("rstudio/shiny")
library(shiny)
runApp(list(ui = fluidPage(
title = 'Row selection in DataTables',
sidebarLayout(
sidebarPanel(textOutput('rows_out')),
mainPanel(dataTableOutput('tbl')),
position = 'right'
)
)
, server = function(input, output) {
output$tbl <- renderDataTable(
mtcars,
options = list(pageLength = 10),
callback = "function(table) {
table.on('click.dt', 'tr', function() {
$(this).toggleClass('selected');
Shiny.onInputChange('rows',
table.rows('.selected').indexes().toArray());
});
}"
)
output$rows_out <- renderText({
paste(c('You selected these rows on the page:', input$rows),
collapse = ' ')
})
}
)
)
for your second example:
library(shiny)
mymtcars = mtcars
mymtcars$id = 1:nrow(mtcars)
runApp(
list(ui = pageWithSidebar(
headerPanel('Examples of DataTables'),
sidebarPanel(
checkboxGroupInput('show_vars', 'Columns to show:', names(mymtcars),
selected = names(mymtcars))
,textInput("collection_txt",label="Foo")
),
mainPanel(
dataTableOutput("mytable")
)
)
, server = function(input, output, session) {
rowSelect <- reactive({
paste(sort(unique(input[["rows"]])),sep=',')
})
observe({
updateTextInput(session, "collection_txt", value = rowSelect() ,label = "Foo:" )
})
output$mytable = renderDataTable({
addCheckboxButtons <- paste0('<input type="checkbox" name="row', mymtcars$id, '" value="', mymtcars$id, '">',"")
#Display table with checkbox buttons
cbind(Pick=addCheckboxButtons, mymtcars[, input$show_vars, drop=FALSE])
}, options = list(orderClasses = TRUE, lengthMenu = c(5, 25, 50), pageLength = 25)
, callback = "function(table) {
table.on('change.dt', 'tr td input:checkbox', function() {
setTimeout(function () {
Shiny.onInputChange('rows', $(this).add('tr td input:checkbox:checked').parent().siblings(':last-child').map(function() {
return $(this).text();
}).get())
}, 10);
});
}")
}
)
)
This answer has been rendered broken in shiny 0.11.1, but can easily be fixed. Here is the update that did it (link):
Added an escape argument to renderDataTable() to escape the HTML entities
in the data table for security reasons. This might break tables from previous
versions of shiny that use raw HTML in the table content, and the old behavior
can be brought back by escape = FALSE if you are aware of the security
implications. (#627)
Thus, to make the previous solutions work, one must specify escape = FALSE as an option to renderDataTable().
I have made an alternative for check boxes in tables based on the previous Answer code and some tweaking of the JQuery / JavaScript.
For anyone who prefers actual data over row numbers i wrote this code that extracts data from the table and shows that as selection. You can deselect by clicking again. It builds on the former Answers that were very helpful to me (THANKS) so i want to share this as well.
It needs a session object to keep the vector alive (scoping). Actually you can get whatever information you want from the table, just dive into JQuery and change the $row.find('td:nth-child(2)') (number is the column number).I needed the info from the Second column but it is up to you. Selection colors is a bit odd if you also change the visible column amount.... selection colors tend to disappear...
I hope this is helpful, works for me (needs to be optimized but no time for that now)
output$tbl <- renderDataTable(
mtcars,
options = list(pageLength = 6),
callback = "function(table) {
table.on('click.dt', 'tr', function() {
if ( $(this).hasClass('selected') ) {
$(this).removeClass('selected');
} else {
table.$('tr.selected').removeClass('selected');
$(this).addClass('selected');
}
var $row = $(this).closest('tr'),
$tdsROW = $row.find('td'),
$tdsUSER = $row.find('td:nth-child(2)');
$.each($tdsROW, function() {
console.log($(this).text());
});
Shiny.onInputChange('rows',table.rows('.selected').indexes().toArray());
Shiny.onInputChange('CELLselected',$tdsUSER.text());
Shiny.onInputChange('ROWselected',$(this).text());
});
}"
)
output$rows_out <- renderUI({
infoROW <- input$rows
if(length(input$CELLselected)>0){
if(input$CELLselected %in% session$SelectedCell){
session$SelectedCell <- session$SelectedCell[session$SelectedCell != input$CELLselected]
}else{
session$SelectedCell <- append(session$SelectedCell,input$CELLselected)
}
}
htmlTXT <- ""
if(length(session$SelectedCell)>0){
for(i in 1:length(session$SelectedCell)){
htmlTXT <- paste(htmlTXT,session$SelectedCell[i],sep="<br/>")
}
}else{htmlTXT <- "please select from the table"}
HTML(htmlTXT)
})
The answers above are outdated. I received error "Error in datatable: The 'callback' argument only accept a value returned from JS()".
Instead, This one works for me.