R shiny: How to add RadioButton in a table? - r

I want to add a choice row at the bottom of table. I searched for some answers but it included many other functions it was hard for me to understand.
library(shiny)
c1 <-c("Time in free flow (mins)",
"Time slowed down by other traffic (mins)",
"Percentage of total time spent with other vehicles close behind",
"Curviness",
"Running costs",
"Toll cost")
c2 <- c('50','10','25%','Moderate','$12.00','$0.00')
c3 <- c('38','5','31%','extreame','$10.50','$3.00')
c4 <- c('62','8','19%','Almost straight','$9.00','$0.50')
c5 <- c('56','15','12%','Moderate','$13.50','$0.00')
t <- cbind(c1,c2,c3,c4,c5)
ui <- fluidPage(
tableOutput("faith")
)
server <- function(input, output, session) {
output$faith <- renderTable({t}, type = "html", bordered = TRUE, striped = TRUE, align = "c")
}
shinyApp(ui = ui, server = server)
I updated some code based on the comments, but still don't know how to get the select value
library(shiny)
library(DT)
c1 <-c("Time in free flow (mins)",
"Time slowed down by other traffic (mins)",
"Percentage of total time spent with other vehicles close behind",
"Curviness",
"Running costs",
"Toll cost")
c2 <- c('50','10','25%','Moderate','$12.00','$0.00')
c3 <- c('38','5','31%','extreame','$10.50','$3.00')
c4 <- c('62','8','19%','Almost straight','$9.00','$0.50')
c5 <- c('56','15','12%','Moderate','$13.50','$0.00')
t <- cbind(c1,c2,c3,c4,c5)
tm <- c('<input type="radio" name="Dec" value="1"/>',
'<input type="radio" name="Dec" value="2"/>',
'<input type="radio" name="Dec" value="3"/>',
'<input type="radio" name="Dec" value="4"/>',
'<input type="radio" name="Dec" value="5"/>')
tmm <- rbind(t,tm)
shinyApp(
ui = fluidPage(
title = 'Radio buttons in a table',
DT::dataTableOutput('foo'),
verbatimTextOutput('sel')
),
server = function(input, output, session) {
output$foo = DT::renderDataTable(
tmm, escape = FALSE, selection = 'none', server = FALSE,
options = list(dom = 't', paging = FALSE, ordering = FALSE),
callback = JS("table.rows().every(function(i, tab, row) {
var $this = $(this.node());
$this.attr('id', this.data()[0]);
$this.addClass('shiny-input-radiogroup');
});
Shiny.unbindAll(table.table().node());
Shiny.bindAll(table.table().node());")
)
output$sel = renderPrint({
str(sapply('Dec', function(i) input[[i]]))
})
}
)
Any suggestion is welcome.
This is what I try to do. Add a single choice row.

I did it!
But still don't know how to submit the select value though observeEvent.
library(shiny)
library(DT)
library(googlesheets4)
c1 <-c("Time in free flow (mins)",
"Time slowed down by other traffic (mins)",
"Percentage of total time spent with other vehicles close behind",
"Curviness",
"Running costs",
"Toll cost")
c2 <- c('50','10','25%','Moderate','$12.00','$0.00')
c3 <- c('38','5','31%','extreame','$10.50','$3.00')
c4 <- c('62','8','19%','Almost straight','$9.00','$0.50')
c5 <- c('56','15','12%','Moderate','$13.50','$0.00')
t <- cbind(c1,c2,c3,c4,c5)
shinyApp(
ui = fluidPage(
title = 'Radio buttons in a table',
DT::dataTableOutput('foo'),
tt <- textOutput('sel')
textOutput('sel'),
actionButton("submit", "submit")
),
server = function(input, output, session) {
m = matrix(
as.character(1:4), nrow = 1, ncol = 4, byrow = TRUE,
dimnames = list(' ', LETTERS[1:4])
)
for (i in seq_len(nrow(m))) {
m[i, ] = sprintf(
'<input type="radio" name="%s" value="%s"/>',
' ', m[i, ]
)
}
m <- cbind('Chioce',m)
m<-rbind(t,m)
output$foo = DT::renderDataTable(
m, escape = FALSE, selection = 'none', server = FALSE,
options = list(dom = 't', paging = FALSE, ordering = FALSE),
callback = JS("table.rows().every(function(i, tab, row) {
var $this = $(this.node());
$this.attr('id', this.data()[0]);
$this.addClass('shiny-input-radiogroup');
});
Shiny.unbindAll(table.table().node());
Shiny.bindAll(table.table().node());")
)
output$sel = renderPrint({
str(sapply(' ', function(i) input[[i]]))
})
observeEvent(input$submit,{
})}
)

Related

R shiny : How to get the callback value from DT::renderDataTable?

I made a select table with button in R shiny.
I want to add a submit actionButton to get the select value from DT::renderDataTable though observeEvent() function.
I have tried
observeEvent(input$submit,{
data <- data.frame(output$sel)}
This is not work.
Is it possible to get the value of callback?
The value can been see in the UI, but how can i get in server?
library(shiny)
library(DT)
library(googlesheets4)
c1 <-c("Time in free flow (mins)",
"Time slowed down by other traffic (mins)",
"Percentage of total time spent with other vehicles close behind",
"Curviness",
"Running costs",
"Toll cost")
c2 <- c('50','10','25%','Moderate','$12.00','$0.00')
c3 <- c('38','5','31%','extreame','$10.50','$3.00')
c4 <- c('62','8','19%','Almost straight','$9.00','$0.50')
c5 <- c('56','15','12%','Moderate','$13.50','$0.00')
t <- cbind(c1,c2,c3,c4,c5)
shinyApp(
ui = fluidPage(
title = 'Radio buttons in a table',
DT::dataTableOutput('foo'),
#tt <- textOutput('sel'),
textOutput('sel'),
actionButton("submit", "submit")
),
server = function(input, output, session) {
m = matrix(
as.character(1:4), nrow = 1, ncol = 4, byrow = TRUE,
dimnames = list(' ', LETTERS[1:4])
)
for (i in seq_len(nrow(m))) {
m[i, ] = sprintf(
'<input type="radio" name="%s" value="%s"/>',
' ', m[i, ]
)
}
m <- cbind('Chioce',m)
m<-rbind(t,m)
output$foo = DT::renderDataTable(
m, escape = FALSE, selection = 'none', server = FALSE,
options = list(dom = 't', paging = FALSE, ordering = FALSE),
callback = JS("table.rows().every(function(i, tab, row) {
var $this = $(this.node());
$this.attr('id', this.data()[0]);
$this.addClass('shiny-input-radiogroup');
});
Shiny.unbindAll(table.table().node());
Shiny.bindAll(table.table().node());")
)
output$sel = renderPrint({
str(sapply(' ', function(i) input[[i]]))
})
observeEvent(input$submit,{
})})
You can get the selected row indices (and work forward from there) like this:
ui <- fluidPage(
verbatimTextOutput('log'),
DTOutput('myTable')
)
server <- function(input, output) {
output$myTable <- renderDataTable(iris)
output$log <- renderPrint({
row_indices <- input$myTable_rows_selected
iris[row_indices,]
})
}
shinyApp(ui, server)
edit
You can set server input values via client side javascript.
In your case:
## [...]
for (i in seq_len(nrow(m))) {
m[i, ] = sprintf(
'<input type="radio" name="%s" value="%s"
// set input$selected to m[i,] when clicked:
onClick = "Shiny.setInputValue(\'selected\', %s)" />',
' ', m[i, ], m[i,]
)
}
## now input$selected will be updated upon selection of either radio input
## [...]
see: https://shiny.rstudio.com/articles/communicating-with-js.html

Implementing Feedback in R / Shiny with DT DataTables rows per select input choice not working / crashing

I am trying to build a feedback system. Here is a simplified example what I am trying to build. I have a DT:datatable that is rendered with a feedback column, based on a selected input choice.
The feedback is submitted through the observeEvent on a submit button. All the UI and server components are mostly as I want.
library(shiny)
library(shinydashboard)
ui <- ui <- dashboardPage(
header = dashboardHeader(title='Car Recommendations'),
sidebar = dashboardSidebar(
width = 450,
fluidRow(
column(
width = 9,
selectInput(
"cyl", 'Select Cylinder Count:',
choices = c('', sort(unique(mtcars$cyl)))
)
)
)
),
body = dashboardBody(
fluidPage(
fluidRow(
uiOutput('rec_ui')
))
)
)
server <- function(input, output, session) {
mtcarsData <- reactive({
req(input$cyl)
mtcars %>%
filter(cyl == input$cyl) %>%
select(am, wt, hp, mpg)
})
output$rec_ui <- renderUI({
mtcarsData()
mainPanel(
actionButton(
'feedbackButton', 'Submit Feedback', class = 'btn-primary'
),
dataTableOutput(('rec')),
width = 12
)
})
feedbackInputData <- reactive({
mtcars <- mtcarsData()
recsInput <- sapply(1:nrow(mtcars), function(row_id)
input[[paste0('rec', row_id)]]
)
})
observeEvent(input$feedbackButton, {
mtcars <- mtcarsData()
feedbackInput <- feedbackInputData()
recFeedbackDf <- bind_rows(
lapply(1:nrow(mtcars), function(row_id)
list(
shiny_session_token = session$token,
recommendation_type = 'CAR',
input_cyl = input$cyl,
recommended_mpg = mtcars$mpg[row_id],
recommendation_feedback = feedbackInput[row_id],
feedback_timestamp = as.character(Sys.time())
)
)
)
write.table(
recFeedbackDf, 'feedback.csv', row.names = FALSE,
quote = FALSE, col.names = FALSE, sep = '|',
append = TRUE
)
showModal(
modalDialog(
'Successfully submitted', easyClose = TRUE,
footer = NULL, class = 'success'
)
)
})
output$rec <- DT::renderDataTable({
df <- mtcarsData()
feedbackCol <- lapply(1:nrow(df), function(recnum)
as.character(
radioButtons(
paste0('rec', recnum), '',
choices = c('neutral' = 'Neutral', 'good' = 'Good', 'bad' = 'Bad'),
inline = TRUE
)
)
)
feedbackCol <- tibble(Feedback = feedbackCol)
df <- bind_cols(
df,
feedbackCol
)
df %>%
DT::datatable(
extensions = 'FixedColumns',
rownames = FALSE,
escape = FALSE,
class="compact cell-border",
options = list(
pageLength = 10,
lengthChange = FALSE,
scrollX = TRUE,
searching = FALSE,
dom = 't',
ordering = TRUE,
fixedColumns = list(leftColumns = 2),
preDrawCallback = JS(
'function() { Shiny.unbindAll(this.api().table().node()); }'
),
drawCallback = JS(
'function() { Shiny.bindAll(this.api().table().node()); } '
),
autoWidth = TRUE,
columnDefs = list(
list(width = '250px', targets = -1)
)
)
)
})
}
shinyApp(ui = ui, server = server)
However, upon submission, one of two things happens:
App crashes with the following error in write.table. But, the root causes is that this line of code is returning a list of NULL values instead of my feedback inputs.
Warning: Error in write.table: unimplemented type 'list' in 'EncodeElement'
feedbackInputData <- reactive({
mtcars <- mtcarsData()
recsInput <- sapply(1:nrow(mtcars), function(row_id)
input[[paste0('rec', row_id)]]
)
})
When the app does not crash, and Feedback gets submitted, but the new inputs don't take effect. Only the first ever submission is repeated written to the CSV.
Any idea where I am going wrong with this app?
Additional Info: It is my hunch that the crash happens when I get from a selection from 'fewer rows' DT to more rows, and not the other way. For example, if I select 8 CYL first, which has more cars, and then 4, the app does not crash on submit. But the reverse, it does. BTW - in either case, my feedback does not get updated.
To avoid the app from crashing write the line
recFeedbackDf <- apply(recFeedbackDf,2,as.character)
just before write.table()
Please note that lapply returns a list, hence your first issue.
Next, recycling input IDs in radio buttons is also an issue. By defining unique IDs, you can make it work. Lastly, to ensure that the radio buttons work all the time, it is best to define new IDs. If the IDs are fixed for a given cyl value, it will only work the first time. Subsequent selection of that cyl will display the initial selection, which can be updated via updateradioButtons, but that will not be reactive. Try this and modify display table to your needs.
library(DT)
library(data.table)
library(shiny)
#library(shinyjs)
library(shinydashboard)
options(device.ask.default = FALSE)
ui <- dashboardPage(
header = dashboardHeader(title='Car Recommendations'),
sidebar = dashboardSidebar(
width = 450,
fluidRow(
column(
width = 9,
selectInput(
"cyl", 'Select Cylinder Count:',
choices = c('', sort(unique(mtcars$cyl)))
)
)
)
),
body = dashboardBody(
#useShinyjs(),
fluidPage(
fluidRow(
actionButton('feedbackButton', 'Submit Feedback', class = 'btn-primary'),
DTOutput('rec'),
verbatimTextOutput("sel")
))
)
)
server <- function(input, output, session) {
cntr <- reactiveVal(0)
rv <- reactiveValues()
mtcarsData <- reactive({
mtcar <- mtcars %>% filter(cyl == input$cyl) %>%
select(cyl, am, wt, hp, mpg)
})
observe({
req(input$cyl,mtcarsData())
mtcar <- mtcarsData()
id <- cntr()
m = data.table(
rowid = sapply(1:nrow(mtcar), function(i){paste0('rec',input$cyl,i,id)}),
Neutral = 'Neutral',
Good = 'Good',
Bad = 'Bad',
mtcar
) %>%
mutate(Neutral = sprintf('<input type="radio" name="%s" value="%s" checked="checked"/>', rowid, Neutral),
Good = sprintf('<input type="radio" name="%s" value="%s"/>', rowid, Good),
Bad = sprintf('<input type="radio" name="%s" value="%s"/>', rowid, Bad)
)
rv$df <- m
print(id)
})
observeEvent(input$cyl, {
cntr(cntr()+1)
#print(cntr())
},ignoreInit = TRUE)
feedbackInputData <- reactive({
dfa <- req(rv$df)
list_values <- list()
for (i in unique(dfa$rowid)) {
list_values[[i]] <- input[[i]]
}
list_values
})
observeEvent(input$feedbackButton, {
req(input$cyl)
mtcar <- rv$df ## this could be mtcarsData(), if picking columns not in rv$df but only in mtcarsData()
dt <- rv$df
dt$Feedback <- feedbackInputData()
recFeedbackDf <- bind_rows(
lapply(1:nrow(mtcar), function(row_id){
list(
shiny_session_token = session$token,
recommendation_type = 'CAR',
input_cyl = input$cyl,
recommended_mpg = mtcar$mpg[row_id],
recommendation_feedback = dt$Feedback[row_id],
feedback_timestamp = as.character(Sys.time())
)
})
)
recFeedbackDf <- apply(recFeedbackDf,2,as.character)
write.table(
recFeedbackDf, 'feedback.csv', row.names = FALSE,
quote = FALSE, col.names = FALSE, sep = '|',
append = TRUE
)
showModal(
modalDialog(
'Successfully submitted', easyClose = TRUE,
footer = NULL, class = 'success'
)
)
})
output$rec <- renderDT(
datatable(
rv$df,
selection = "none",
escape = FALSE,
options = list(
columnDefs = list(list(visible = FALSE, targets = c(0,4))), ## not displaying rowid and cyl
dom = 't',
paging = FALSE,
ordering = FALSE
),
callback = JS(
"table.rows().every(function(i, tab, row) {
var $this = $(this.node());
$this.attr('id', this.data()[0]);
$this.addClass('shiny-input-radiogroup');
});
Shiny.unbindAll(table.table().node());
Shiny.bindAll(table.table().node());"
),
rownames = F
),
server = FALSE
)
### verify if the radio button values are being returned
output$sel = renderPrint({
req(feedbackInputData())
feedbackInputData()
})
}
shinyApp(ui = ui, server = server)

render dropdown for single column in DT shiny

I'm not proficient in Javascript and would like to replicate a dropdown function as is available in the rhandsontable package but for the DT package.
How could this be achieved in the most efficient way?
Example
library(DT)
i <- 1:5
datatable(iris[1:20, ],
editable = T,
options = list(
columnDefs = list(
list(
targets = 5,
render = JS(
# can't get my head around what should be in the renderer...
)
))
))
The goal is to have the i variable act as validator for the allowed input in the DT object.
Any help is much appreciated!
I blatantly stole the idea from Yihui's app for including radioButtons in DT.
Code:
library(shiny)
library(DT)
ui <- fluidPage(
title = 'Selectinput column in a table',
h3("Source:", tags$a("Yihui Xie", href = "https://yihui.shinyapps.io/DT-radio/")),
DT::dataTableOutput('foo'),
verbatimTextOutput('sel')
)
server <- function(input, output, session) {
data <- head(iris, 5)
for (i in 1:nrow(data)) {
data$species_selector[i] <- as.character(selectInput(paste0("sel", 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),
callback = JS("table.rows().every(function(i, tab, row) {
var $this = $(this.node());
$this.attr('id', this.data()[0]);
$this.addClass('shiny-input-container');
});
Shiny.unbindAll(table.table().node());
Shiny.bindAll(table.table().node());")
)
output$sel = renderPrint({
str(sapply(1:nrow(data), function(i) input[[paste0("sel", i)]]))
})
}
shinyApp(ui, server)
Output:

DT Filtering not returning row number

In the code below, I am saving user selected rows into CSV file. It is working perfectly fine. Problem arises if I add filter='top', selection = 'multiple' in datatable() function then it stops returning row value (row position). The user-defined function rowSelect() returns row value.
See the complete code below -
library(shiny)
library(RODBC)
library(DT)
library(shinyalert)
mydata = mtcars
mydata$id = 1:nrow(mydata)
d = data.frame(stringsAsFactors = F)
runApp(
list(ui = pageWithSidebar(
headerPanel('Examples of Table'),
sidebarPanel(
textInput("collection_txt",label="RowIndex")
,br(),useShinyalert(),
actionButton("run", "Write Data"),
br(),
p("Writeback with every user input. CSV file gets saved on your working directory!")),
mainPanel(
DT::dataTableOutput("mytable")
))
, server = function(input, output, session) {
shinyInput <- function(FUN,id,num,...) {
inputs <- character(num)
for (i in seq_len(num)) {
inputs[i] <- as.character(FUN(paste0(id,i),label=NULL,...))
}
inputs
}
rowSelect <- reactive({
rows=names(input)[grepl(pattern = "srows_",names(input))]
paste(unlist(lapply(rows,function(i){
if(input[[i]]==T){
return(substr(i,gregexpr(pattern = "_",i)[[1]]+1,nchar(i)))
}
})))
})
observe({
updateTextInput(session, "collection_txt", value = rowSelect() ,label = "RowIndex:" )
})
df_subset <- reactive({
d = data.frame(n = rowSelect(), stringsAsFactors = F)
return(d)
})
observeEvent(input$run, {write.csv(mydata[as.numeric(df_subset()$n),], file = "Writeback.csv" , row.names=F)
shinyalert(title = "Task Completed!", type = "success")})
output$mytable = DT::renderDataTable({
DT::datatable(cbind(Flag=paste0('<input type="checkbox" id="srows_', mydata$id, '" value="', mydata$id, '">',""),
mydata), extensions = 'Buttons', options = list(orderClasses = TRUE,
pageLength = 5, lengthChange = FALSE, dom = 'Bfrtip',
buttons = c('copy', 'csv', 'excel'),
drawCallback= JS(
'function(settings) {
Shiny.unbindAll(this.api().table().node());
Shiny.bindAll(this.api().table().node());}')
),escape=F)
}
)
}), launch.browser = T
)
Any help would be highly appreciated!

Extracting values of selected radio buttons in shiny DT

Background : I have a shiny DT in a modaldialog box conataining radio buttons in columns, I want to do some processing based on the selections of radio button in DT
Problem : I am unable to figure out how to extract the value of the selected radiobutton from DT
Below is the reproducible exmaple for the same.
library(shiny)
library(DT)
library(data.table)
library(shinyWidgets)
shinyApp(
ui = fluidPage(
title = 'Radio buttons in a table',
actionBttn(inputId = "btnContinue",label = "Continue",style = "material-flat")
),
server = function(input, output, session) {
dtWithRadioButton <- reactiveValues(dt = NULL)
observeEvent(input$btnContinue,{
m = matrix(
as.character(1:5), nrow = 12, ncol = 5, byrow = TRUE,
dimnames = list(month.abb, LETTERS[1:5])
)
for (i in seq_len(nrow(m))) {
m[i, ] = sprintf(
'<input type="radio" name="%s" value="%s"/>',
month.abb[i], m[i, ]
)
}
dt <- data.table(m)
v <- LETTERS[1:12]
dt <- cbind(v,dt)
dtWithRadioButton$dt <- dt # setting reactive value
showModal(modalDialog(
size = "l",easyClose = F,fade = T,
renderDataTable(datatable(dt, selection = "none",escape = FALSE, options = list(dom = 't') , rownames = F)),
footer = tagList(
actionBttn(inputId = "btnCancel",label = "Cancel",style = "float",size="sm",color="warning"),
actionBttn(inputId = "btnProcess",label = "Process",style = "float",size="sm",color="success")
)
))
})
observeEvent(input$btnProcess,{
dt <- dtWithRadioButton$dt # accessing the reactive value
# do some processing based on the radio button selection
})
observeEvent(input$btnCancel,{
removeModal(session)
})
}
)
On click of 'Continue' button a pop-up containing a shiny DT with radio button is displayed to user. Once user makes selection using radio button. I want to run a process on click of btnProcess
You can do:
library(shiny)
library(DT)
library(shinyWidgets)
m <- matrix(
as.character(1:5), nrow = 12, ncol = 5, byrow = TRUE,
dimnames = list(month.abb, LETTERS[1:5])
)
for (i in seq_len(nrow(m))) {
for(j in seq_len(ncol(m))) {
m[i, j] <- sprintf(
'<input type="radio" name="%s" value="%s" %s/>',
month.abb[i], m[i, j], ifelse(j==1, 'checked="checked"', "")
)
}
}
shinyApp(
ui = fluidPage(
title = 'Radio buttons in a table',
actionBttn(inputId = "btnContinue", label = "Continue",
style = "material-flat")
),
server = function(input, output, session) {
dtWithRadioButton <- reactiveValues(dt = m)
observeEvent(input$btnContinue,{
showModal(modalDialog(
size = "l", easyClose = FALSE, fade = TRUE,
DTOutput("datatable"),
footer = tagList(
actionBttn(inputId = "btnCancel", label = "Cancel",
style = "float", size="sm", color="warning"),
actionBttn(inputId = "btnProcess", label = "Process",
style = "float", size="sm", color="success")
)
))
})
output$datatable <- renderDT(
datatable(dtWithRadioButton$dt, selection = "none", escape = FALSE,
options = list(
dom = 't',
paging = FALSE,
ordering = FALSE
),
callback = JS(
"table.rows().every(function(i, tab, row) {
var $this = $(this.node());
$this.attr('id', this.data()[0]);
$this.addClass('shiny-input-radiogroup');
});
Shiny.unbindAll(table.table().node());
Shiny.bindAll(table.table().node());"),
rownames = TRUE),
server = FALSE
)
observeEvent(input$btnProcess,{
dt <- dtWithRadioButton$dt # accessing the reactive value
# do some processing based on the radio button selection
for(month in month.abb){
print(paste0(month, ": ", input[[month]]))
}
})
observeEvent(input$btnCancel,{
removeModal(session)
})
}
)
Then the value of the selected button is in input$Jan for the first row, input$Feb for the second row, etc.

Resources