Limit row selection in DT Table in Shiny - r

I am currently trying to limit my selection in a DataTable in Shiny to just two rows - I want the table to not allow the user to click on more than rows (but also to have the ability to deselect them afterwards).
library(DT)
shinyApp(
ui = fluidPage(
fluidRow(
column(12,
dataTableOutput('table')
)
)
),
server = function(input, output) {
output$table <- DT::renderDataTable(iris,
options = list(selection = "multiple")
)
}
)
The row selection is currently on multiple mode, which works, but I don't want the selection to exceed two rows.

Update: Does not seem to work anymore, since 04.2022 or earlier.
You could either solve it via javascript, which you may have seen already:
Limit row selection to 3 in datatables
Or you update the datatable in Shiny:
library(DT)
library(shiny)
shinyApp(
ui = fluidPage(
fluidRow(
column(12,dataTableOutput('tbl'))
)
),
server = function(input, output) {
reset <- reactiveValues(sel = "")
output$tbl <- DT::renderDataTable({
input$tbl_rows_selected
datatable(iris, selection = list(mode = 'multiple', selected = reset$sel))
})
observe({
if(length(input$tbl_rows_selected) > 2){
reset$sel <- setdiff(input$tbl_rows_selected, input$tbl_row_last_clicked)
}else{
reset$sel <- input$tbl_rows_selected
}
})
}
)
This solution might be less clean, but a bit easier to understand.

It's not exactly what you want but I've changed a bit Tonio's answer, it may help someone else.
library(DT)
library(shiny)
shinyApp(
ui = fluidPage(
fluidRow(
column(12,dataTableOutput('tbl'))
),
textOutput('selected_rows')
),
server = function(input, output) {
reset <- reactiveValues(sel = "")
output$tbl <- DT::renderDataTable({
datatable(iris, selection = list(mode = 'multiple', selected = reset$sel))
})
observe({
if(length(input$tbl_rows_selected) > 2){
reset$sel <- NULL
}else{
reset$sel <- input$tbl_rows_selected
}
})
output$selected_rows <- renderText({input$tbl_rows_selected})
}
)

Related

R Shiny - Unable to display data table in mainpanel based on "selectInput" user selection

I'm trying to display a table in the mainpanel based on selection by the user from drop down.
Ex. The "selectInput" dropdown will have the values from table789 (options are 123, xyz, ...)
If user selects 123 then "table123" has to be displayed else "table456" to be displayed.
I'm very new to shiny, tried the below and got "attempt to replicate an object of type 'closure'" error. I'm not sure what I'm missing. Can anyone help?
Server:
server = function(input, output, session) {
output$outputtable = reactive({ifelse(input$selction == '123', DT::renderDataTable(table123), DT::renderDataTable(table456)) })
}
UI:
ui <- fluidPage(
sidebarPanel(selectInput("selction", "Select", choices = table789$column1, selected = "xyz")),
mainPanel(
tabsetPanel(
tabPanel("select", DT::dataTableOutput("outputtable")),
)
)
)
I think you're mixing things up here, you shouldn't be putting renderDataTable into reactive, this should work:
library(shiny)
table789 <- mtcars
table456 <- iris
table123 <- mtcars
table789$column1 <- sample(c("123","456"),nrow(table789),replace = T)
ui <- fluidPage(
sidebarPanel(selectInput("selection", "Select", choices = unique(table789$column1), selected = "xyz")),
mainPanel(
tabsetPanel(
tabPanel("select",
DT::dataTableOutput("outputtable")
)
)
)
)
server = function(input, output, session) {
data <- eventReactive(input$selection,{
if(input$selection == "123"){
return (table123)
}
table456
})
output$outputtable <- DT::renderDataTable(
data()
)
}
#Run the app
shinyApp(ui = ui, server = server)

data table disappear after editing in shiny app

I'm trying to make an editable and downloadable data table in shiny app. After I edit the table, the data table automatically disappear for some reason. This only happen when the data dat is reactive (which is necessary in my app).
Does anyone knows what is going on? Thanks a lot.
example code below:
library(shiny)
library(DT)
ui <- fluidPage(
selectInput("nrow",
"num of rows",
choices = 1:5,
selected = 3,
multiple = FALSE),
DTOutput("table")
)
server <- function(input, output){
dat = reactive({
iris[1:as.integer(input$nrow),]
})
output[["table"]] <- renderDT({
datatable(dat(), editable = "cell", extensions = "Buttons",
options = list(
dom = "Bfrtip",
buttons = list(
"csv"
)
))
})
observeEvent(input[["table_cell_edit"]], {
cellinfo <- input[["table_cell_edit"]]
dat() <<- editData(dat(), input[["table_cell_edit"]], "table")
})
}
shinyApp(ui, server)
Try this:
library(shiny)
library(DT)
ui <- fluidPage(
selectInput("nrow","num of rows",choices = 1:5,selected = 3,multiple = FALSE),
DTOutput("table")
)
server <- function(input, output){
v <- reactiveValues()
observeEvent(input$nrow,{
v$dat <- iris[1:as.integer(input$nrow),]
})
output[["table"]] <- renderDT({
datatable(v$dat, editable = "cell", extensions = "Buttons", options = list(dom = "Bfrtip",buttons = list("excel")))
})
observeEvent(input[["table_cell_edit"]], {
cellinfo <- input[["table_cell_edit"]]
v$dat <<- editData(v$dat, input[["table_cell_edit"]], "table")
})
}
shinyApp(ui, server)
Is it OK like this ? A possible unwanted behavior is that the table is reset after changing the number of rows. But I don't think we can avoid that... since these are two different tables.
library(shiny)
library(DT)
ui <- fluidPage(
selectInput("nrow",
"num of rows",
choices = 1:5,
selected = 3,
multiple = FALSE),
DTOutput("table")
)
server <- function(input, output){
dat0 <- iris
dat <- reactiveVal()
observe({
dat(dat0[1:as.integer(input$nrow),])
})
output[["table"]] <- renderDT({
datatable(dat(), editable = "cell", extensions = "Buttons",
options = list(
dom = "Bfrtip",
buttons = list(
"csv"
)
))
})
observeEvent(input[["table_cell_edit"]], {
cellinfo <- input[["table_cell_edit"]]
dat(editData(dat(), input[["table_cell_edit"]], "table"))
})
}
shinyApp(ui, server)

RShiny: DT select rows and assign to group

I would like to give users an option to assign different rows (subjects) to groups.
Ideally, one can highlight rows, then write a group name in the "Assign to group: " field and it is saved. Then they can select a new set of rows and add those to a different group; and so on until all desired rows are assigned.
Here is what I have so far. I can't figure out how to save the results before selecting a new set of rows..
library(shiny)
library(DT)
ui <- fluidPage(
fluidRow(
column(6,
DT::dataTableOutput('x1'),
textInput("assignGroup","Assign to group: ")),
column(6, DT::dataTableOutput('x2'))
)
)
server <- shinyServer(function(input, output, session) {
output$x1 = DT::renderDataTable(cars, server = FALSE)
output$x2 = DT::renderDataTable({
s <- input$x1_rows_selected
temp <- cars
temp$Experiment <- as.character("")
temp[s,"Experiment"] <- input$assignGroup
temp
}, server = FALSE)
})
shinyApp(ui, server)
Thanks and hope this is somewhat clear!!!
You can observe 'input$x1_rows_selected' and then edit the table on the event. To display live changes to the table, you can add a reactive table.
Hope this code works out for you.
library(shiny)
library(DT)
ui <- fluidPage(
fluidRow(
column(6,
DT::dataTableOutput('x1'),
textInput("assignGroup","Assign to group: ")
),
column(6, DT::dataTableOutput('x2'))
)
)
server <- shinyServer(function(input, output, session) {
values <- reactiveValues(
df_data = cars,
temp = {
cars[,c("Experiment")]<-NA
cars
}
)
output$x1 = DT::renderDataTable(values$df_data, server = FALSE)
observeEvent(input$x1_rows_selected,
{
s<-input$x1_rows_selected
values$temp[s,"Experiment"]<-input$assignGroup
}
)
output$x2 = DT::renderDataTable(
values$temp, server = FALSE)
})
shinyApp(ui, server)

Wrap a reactive UI in an action button RShiny

I have a tabbed UI that shows up whenever the user selects rows in a datatable (in the following code, the outputs are random, in real life the calculation is quite involved).
I would like to condition the tabbed UI showing up to the click of a button. Currently every time you select an additional row, it does the calculation all over again for the already selected rows. I would like to limit that to a one-time calculation when the user is done selecting all the rows he wants to see.
library(shiny)
library(DT)
The UI : the table, the action button and the tabbed section.
ui <- fluidPage(
mainPanel(
fluidRow(
column(12,DT::dataTableOutput(outputId = 'tableCurrencies'))
),
actionButton(inputId = 'showSelectedButton',label = 'Show Selec'),
fluidRow(
uiOutput("myTabUI")
)
)
)
The server function : If I remove the output$myTabUI <- eventReactive(input$launchCalcButton, { part and instead do output$myTabUI <- renderUI ({ ... directly it works as intended (minus the calculation following click on the button of course).
server <- function(input,output){
output$tableCurrencies <- DT::renderDataTable({datatable(data.frame(a=rnorm(10),b=rnorm(10),c=rnorm(10)))})
origTable_selected <- reactive({
ids <- input$tableCurrencies_rows_selected
return(ids)
})
output$myTabUI <- eventReactive(input$launchCalcButton, {
selectedTabs <- renderUI({
myTabs <- lapply(origTable_selected(),function(i) {
tabName <- paste0("test",i)
a <- renderPlot({
hist(rnorm(50))
})
output[[paste0(tabName,"rates")]] <- a
return(tabPanel(
tabName,
fluidRow(
column(6,plotOutput(paste0(tabName,"rates")))
)
))
})
return(do.call(tabsetPanel,myTabs))
})
selectedTabs
})
}
app = shinyApp(ui,server)
runApp(app,port=3250,host='0.0.0.0')
Not sure how to go about fixing this. Any help welcome.
You can use isolate() to limit reactive dependencies
library(shiny)
library(DT)
ui <- fluidPage(
mainPanel(
fluidRow(
column(12,DT::dataTableOutput(outputId = 'tableCurrencies'))
),
actionButton(inputId = 'showSelectedButton',label = 'Show Selec'),
fluidRow(uiOutput("myTabUI"))
)
)
server <- function(input,output){
output$tableCurrencies <- DT::renderDataTable({
data.frame(a=rnorm(10),b=rnorm(10),c=rnorm(10))})
origTable_selected <- reactive({
input$tableCurrencies_rows_selected
})
output$myTabUI <- renderUI({
input$showSelectedButton
myTabs <- lapply(isolate(origTable_selected()),function(i) {
tabName <- paste0("test",i)
a <- renderPlot({hist(rnorm(50))})
output[[paste0(tabName,"rates")]] <- a
return(tabPanel(
tabName,
fluidRow(column(6,plotOutput(paste0(tabName,"rates"))))
))
})
do.call(tabsetPanel,myTabs)
})
}
shinyApp(ui,server)

Output more than 1 datatables in shiny main panel

I have a shiny app that a user can check whether they want the data table displayed in the main panel. Depending on the numericinput, if they select 1, only 1 datatable be displayed or if they select 2 it will display 2 datatables I am not so sure how to code this in shiny R since I am new to this. Thank you for looking into this.
Here is my code
library("shiny")
df1 <- data.frame("2010-01"=double(),
"2010-02"=double(),
"2010-03"=double(),
"2010-04"=double()
)
df1<-rbind(df1,setNames(as.list(c(10,20,30,40)), names(df2)))
df2 <- data.frame("2010-01"=double(),
"2010-02"=double(),
"2010-03"=double(),
"2010-04"=double()
)
df2<-rbind(df2,setNames(as.list(c(100,200,300,400)), names(df2)))
df3 <- data.frame("2010-01"=double(),
"2010-02"=double(),
"2010-03"=double(),
"2010-04"=double()
)
df3<-rbind(df3,setNames(as.list(c(1000,2000,3000,4000)), names(df2)))
ui <-fluidPage(
sidebarPanel(
checkboxInput("add_data", "Add Data Table(s)"),
conditionalPanel(condition="input.add_data === true",
numericInput("numofdata",
label="Number of Data Table(s):",
min = 1,
max = 3,
value = 1,
step = 1),
uiOutput("num_of_data"),
textOutput("see_ranges")
),
actionButton("submit", "Submit")
),
mainPanel(
titlePanel("Output Data Table"),
DT::dataTableOutput("datatable.view", width = "95%")
) # end of main panel
)
server <- function(input, output, session) {
output$num_of_data <- renderUI({
lapply(1:input$numofdata, function(i) {
print(trend_list())
})
})
output$see_ranges <- renderPrint({
print(trend_list())
})
data.filter <- reactive({
df(i)
})
output$datatable.view <- DT::renderDataTable(
{
input$submit
if (input$submit==0) return()
isolate({
for(i in 1:input$numoftrends) {
datatable(data.filter(i),
rownames=FALSE,
extensions = c("FixedColumns", "FixedHeader", "Scroller"),
options = list(searching=FALSE,
autoWidth=TRUE,
rownames=FALSE,
scroller=TRUE,
scrollX=TRUE,
pagelength=1,
fixedHeader=TRUE,
class='cell-border stripe',
fixedColumns =
list(leftColumns=2,heightMatch='none')
)
)
}
})
})
}
shinyApp(ui = ui, server = server)
You should look at this article:
http://shiny.rstudio.com/gallery/creating-a-ui-from-a-loop.html
You will seen then that one has to create multiple renderDataTable instead of muliple datatable within one renderDataTable().
Also in your code you call df like a function df() but it is only defined as a variable.
See a generic running example below.
EDIT: Changed dynamic part of UI.
library(DT)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("amountTable", "Amount Tables", 1:10, 3)
),
mainPanel(
# UI output
uiOutput("dt")
)
)
)
server <- function(input, output, session) {
observe({
lapply(1:input$amountTable, function(amtTable) {
output[[paste0('T', amtTable)]] <- DT::renderDataTable({
iris[1:amtTable, ]
})
})
})
output$dt <- renderUI({
tagList(lapply(1:input$amountTable, function(i) {
dataTableOutput(paste0('T', i))
}))
})
}
shinyApp(ui, server)

Resources