I am trying to reset selectizeInput selections upon actionButton input.
Please see the following code, in which I cannot get the eventReactive to function:
library(shiny)
ui <- fluidPage(
column(width = 4,algin = "center", uiOutput("choose_Number")) ,
br(),
column(width = 4, algin = "center",div(
align = "center", actionButton('delete','Delete Number(s)',style="color: #fff; background-color: #53C1BE")))
)
server <- function(input, output, session) {
output$choose_Number <- renderUI({
selectizeInput("choose_Number", "Select Number", as.list(c(1,2,3,4)),selected = c(''), options=list(create=TRUE,'plugins' = list('remove_button'),
persist = FALSE), multiple = TRUE)
})
##### I am trying to reset the selectizeInput upon input from the Delete button
eventReactive(input$delete, {updateSelectizeInput("choose_Number", "Select Number", as.list(c(1,2,3,4)), selected = c(''),options=list(create=TRUE,'plugins' = list('remove_button'),
persist = FALSE), multiple = TRUE)} )
}
shinyApp(ui, server)
Thank you.
The biggest issue was that the session was missing. You can omit the session argument if you want to but then you need to name all other arguments because session is the first in line. Second issue was that you can set multiple in selectizeInput but not later when using updateSelectizeInput() to change it.
Minor improvements: the manual recommends using character() to deselect the current choice. As mentioned by #YBS you do not provide a reactive value so that observeEvent is the better choice here.
PS: are you sure you want to centre align the columns? It looks strange.
library(shiny)
ui <- fluidPage(
column(width = 4, align = "center", uiOutput("choose_Number")),
br(),
column(width = 4, align = "center", div(
align = "center",
actionButton('delete', 'Delete Number(s)', style="color: #fff; background-color: #53C1BE"))
)
)
server <- function(input, output, session) {
output$choose_Number <- renderUI({
selectizeInput("choose_Number", "Select Number", as.list(c(1,2,3,4)), selected = character(),
options = list(create=TRUE,'plugins' = list('remove_button'), persist = FALSE),
multiple = TRUE)
})
##### I am trying to reset the selectizeInput upon input from the Delete button
observeEvent(input$delete, {
updateSelectizeInput(session, "choose_Number", choices = as.list(1:4),
selected = character(0),
options = list(create=TRUE, 'plugins' = list('remove_button'), persist = FALSE))
})
}
shinyApp(ui, server)
Related
In the following bit of MWE code using the selectizeInput() function, the user selects which variable to filter the mtcars data by. When running the code you'll see where you can select the filtering variable in the user input box labeled "Variable".
However, how can this be changed so the user can deselect a variable after it has been entered into the "Variable" user input box, and thus update the table? By clicking on a little "x" next to the items appearing in the box would be ideal. For example, I have used selectizeGroupUI() before and those selected items can be easily deselected, and the related table updated, by clicking on a little "x" that appears next to each item.
library(shiny)
shinyApp(
ui = fluidPage(
uiOutput("filter"),
tableOutput("data")
),
server = function(input, output) {
output$filter <-
renderUI({
selectizeInput("variable", "Variable:",
c("Cylinders" = "cyl","Trans" = "am","Gears" = "gear"),
multiple = TRUE
)
})
output$data <- renderTable({
req(input$variable)
mtcars[, c("mpg", input$variable), drop = FALSE]
}, rownames = TRUE)
}
)
Is pickerInput from {shinyWidgets} an option?.
Doc: https://dreamrs.github.io/shinyWidgets/reference/pickerInput.html
pickerInput("variable",
"Variable:",
choices = c("Cylinders" = "cyl","Trans" = "am","Gears" = "gear"),
multiple = TRUE,
selected = NULL,
options = list(
title = "Select variables",
`actions-box` = TRUE,
`deselect-all-text` = "Remove"
))
I am building a Shiny App where users can filter out certain projects. I want the project names to appear in the dropdown only if they appear within a certain date range.
I've been able to populate the selectize menu and have been able to make it so users can select all or remove all projects (from the answer to a question I asked previously). However, now that I'm trying to make these names reactive to the date, the observeEvent code from my previous question crashes. I tried to wrap it in a reactive expression, but then nothing happens.
How do I make my projects filterable by date while still keeping the select all and remove all functionality?
library(shiny)
library(plotly)
library(shinyjs)
library(shinydashboard)
library(shinyWidgets)
library(dplyr)
library(htmltools)
library(lubridate)
ui = fluidPage(
tabsetPanel(
tabPanel("View 1", fluid = TRUE,
sidebarLayout(
sidebarPanel(
h4("Select Your Desired Filters"),
div(id = "inputs",
dateRangeInput(
inputId = "date_filter",
label = "Filter by Month and Year",
start = today(),
end = (today() + 90),
min = "2021-04",
max = NULL,
format = "yyyy-mm",
startview = "month",
weekstart = 0,
language = "en",
separator = " to ",
width = NULL,
autoclose = TRUE
),
br()),
h5("Include/Exclude Specific Projects"),
selectizeInput(inputId = "filter_by_project",
label = "Filter by Project",
choices = sort(unique(test$project)),
multiple = TRUE,
selected = sort(unique(test$project))),
actionButton(inputId = "remove_all",
label = "Unselect All Projects", style = "color: #FFFFFF; background-color: #CA001B; border_color: #CA001B"),
actionButton(inputId = "add_all",
label = "Select All Projects", style = "color: #FFFFFF; background-color: #CA001B; border_color: #CA001B")
),
mainPanel(
)
)
)
)
)
server = function(input, output, session) {
#Here's the dataset
test <- tibble(project = c("Justin", "Corey","Sibley"),
date = ymd(c("2021-04-20", "2021-04-01", "2021-05-05")),
april_2021 = c(10, 100, 101),
may_2021 = c(1, 4, 7))
#I want users to be able to filter the list of projects by date, which should update the selectize options
test <- reactive({
test %>%
dplyr::filter(date >= input$date_filter[1],
date <= input$date_filter[2])
})
observeEvent(input$remove_all, {reactive({
updateSelectizeInput(session,"filter_by_project",choices=sort(unique(test()$project)),
selected=NULL, options = list(placeholder="Please Select at Least One Project")
)
})
})
observeEvent(input$add_all, {reactive({
updateSelectizeInput(session,"filter_by_project",choices=sort(unique(test()$project)), selected=sort(unique(test()$project)) )
})
})
}
shinyApp(ui = ui, server = server)
You have to major problems. First is using the same name for your input data.frame and for your reactive element. You've called them both test which causes confusion as to whether you are trying to use the data.frame or the reactive object. You should use different names. The second problem is you do not need to use reactive() for your observeEvents() calls. You just need to put the code you want to run in a block.
Fixing these problems, your server functon should look more like this
server = function(input, output, session) {
#Here's the dataset
testdata <- tibble(project = c("Justin", "Corey","Sibley"),
date = ymd(c("2021-04-20", "2021-04-01", "2021-05-05")),
april_2021 = c(10, 100, 101),
may_2021 = c(1, 4, 7))
#I want users to be able to filter the list of projects by date, which should update the selectize options
test <- reactive({
testdata %>%
dplyr::filter(date >= input$date_filter[1],
date <= input$date_filter[2])
})
observeEvent(input$remove_all, {
updateSelectizeInput(session,"filter_by_project", choices=sort(unique(test()$project)),
selected=NULL, options = list(placeholder="Please Select at Least One Project")
)
})
observeEvent(input$add_all, {
updateSelectizeInput(session,"filter_by_project", choices=sort(unique(test()$project)), selected=sort(unique(test()$project)) )
})
}
The app below contains a selectInput of dataset IDs and a button View details which displays a modalDialog when clicked. The modal dialog has a datatable that contains some information about the datasets in the selectInput dropdown.
Here is a screenshot of the app on startup:
Since the user can select a dataset either by selecting an option from the dropdown menu or by selecting a row in the datatable, I created a reactive value rv$selectedRow which stores the value of the selected dataset. When the modal is triggered, rv$selectedRow takes the value of input$data. When the Select button in the modal footer is clicked, rv$selectedRow takes the value of input$dfs_rows_selected and the selectInput is updated to reflect this new value. This is done by the two observeEvents in the code below.
When the user selects a row, closes the modal and opens it again, I would like the page and row of the selected dataset (input$data) to be pre-selected. I tried to achieve this using selection = list(mode = 'single', selected = rv$selectedRow) in the renderDT call. As you can see in the screenshot, row 1 should be pre-selected but it isn't. I feel like I'm missing a req() somewhere in the renderDT but I'm not sure. The value of rv$selectedRow checks out when I print it to the console, so I don't know why the selected argument of renderDT isn't working. I am also not sure how to store the page of the selected row. Any insight would be much much appreciated as I'm a little lost.
The app is as follows:
library(shiny)
library(DT)
datasets = data.frame(cbind(id = seq_len(4), name = c('iris', 'mtcars', 'satellite', 'credit')))
# UI ----------------------------------------------------------------------
ui = fluidPage(
selectInput('data', 'Select dataset:', choices = datasets$id),
actionButton('view', 'View details')
)
# SERVER ------------------------------------------------------------------
server <- shinyServer(function(input, output, session) {
rv = reactiveValues(selectedRow = NULL, selectedPage = NULL)
# Opening the modal
observeEvent(input$view, {
rv$selectedRow = req(input$data)
print(paste("selectedRow on 'View':", rv$selectedRow))
showModal(modalDialog(
title = 'Available datasets',
tags$b('Click on a row to select a dataset.'),
br(),
br(),
DT::dataTableOutput('dfs'),
easyClose = F,
footer = tagList(
modalButton('Cancel'),
bsButton('select', 'Select')
)
)
)
})
# Rendering the DT - pre-selection of row not working
output$dfs <- renderDT({
print(paste("selectedRow on 'renderDT':", rv$selectedRow))
datasets
},
options = list(
# displayStart = selectedPage,
pageLength = 2
),
filter = 'top',
selection = list(mode = 'single', selected = rv$selectedRow),
rownames = F
)
# Saving the selected row and updating the selectInput
observeEvent(input$select, {
rv$selectedRow = req(input$dfs_rows_selected)
print(paste("selectedRow on 'Select':", rv$selectedRow))
updateSelectInput(session = session, inputId = 'data', selected = datasets[rv$selectedRow, 1])
removeModal(session)
})
})
shinyApp(ui, server)
Updated code:
As per this solution and the one posted by Wilmar below, using datatable() in the renderDT seemed to fix the problem -
library(shiny)
library(DT)
datasets = data.frame(cbind(id = seq_len(4), name = c('iris', 'mtcars', 'satellite', 'credit')))
# UI ----------------------------------------------------------------------
ui = fluidPage(
selectInput('data', 'Select dataset:', choices = datasets$id),
actionButton('view', 'View details')
)
# SERVER ------------------------------------------------------------------
server <- shinyServer(function(input, output, session) {
rv = reactiveValues(selectedRow = NULL, selectedPage = NULL)
# Opening the modal
observeEvent(input$view, {
print(paste("selectedRow on 'View':", rv$selectedRow))
showModal(modalDialog(
title = 'Available datasets',
tags$b('Click on a row to select a dataset.'),
br(),
br(),
DT::dataTableOutput('dfs'),
easyClose = F,
footer = tagList(
modalButton('Cancel'),
bsButton('select', 'Select')
)
)
)
})
# Rendering the DT - pre-selection of row not working
output$dfs <- renderDataTable({
r = rv$selectedRow
print(paste("selectedRow on 'renderDT':", r))
datatable(
datasets,
options = list(
displayStart = as.numeric(r)-1,
pageLength = 2
),
filter = 'top',
selection = list(mode = 'single', selected = r),
rownames = F
)
}, server = F)
# Saving the selected row and updating the selectInput
observeEvent(input$select, {
rv$selectedRow = req(input$dfs_rows_selected)
print(paste("selectedRow on 'Select':", rv$selectedRow))
updateSelectInput(session = session, inputId = 'data', selected = datasets[rv$selectedRow, 1])
removeModal(session)
})
observe({
rv$selectedRow = input$data
})
})
shinyApp(ui, server)
I guess this is what you're looking for. Your first problem was that you had to convert rv$selectedRow to numeric. Secondly it you were re-rendering your datatable everytime you pressed the "view" button. And thirdly you didn't do anything with your selectInput ("data").
I transformed rv$selectedRow to a numeric, moved your showModal to the ui and created an observer for your selectInput. In addition, I wrapped your datafarme in the datatable function, which I think is a bit more convenient.
Working example:
library(shiny)
library(DT)
library(shinyBS)
datasets = data.frame(cbind(id = seq_len(4), name = c('iris', 'mtcars', 'satellite', 'credit')))
# UI ----------------------------------------------------------------------
ui = fluidPage(
selectInput('data', 'Select dataset:', choices = datasets$id),
actionButton('view', 'View details'),
tags$head(tags$style("#df_popup .modal-footer{ display:none}
#df_popup .modal-header .close{display:none}")),
bsModal("df_popup", title='Available datasets', trigger='view',
tags$b('Click on a row to select a dataset.'),
br(),
br(),
DT::dataTableOutput('dfs'),
column(12, align='right',
modalButton('Cancel'),
bsButton('select', 'Select')
)
)
)
# SERVER ------------------------------------------------------------------
server <- shinyServer(function(input, output, session) {
rv = reactiveValues(selectedRow = NULL, selectedPage = NULL)
# Rendering the DT - pre-selection of row not working
output$dfs <- renderDT({
print(paste("selectedRow on 'renderDT':", rv$selectedRow))
datatable(datasets, options = list(
# displayStart = selectedPage,
pageLength = 2
),
filter = 'top',
selection = list(mode = 'single', selected=c(as.numeric(rv$selectedRow))),
rownames = F)
},
)
# Saving the selected row and updating the selectInput
observeEvent(input$select, {
rv$selectedRow = req(input$dfs_rows_selected)
print(paste("selectedRow on 'Select':", rv$selectedRow))
updateSelectInput(session = session, inputId = 'data', selected = datasets[rv$selectedRow, 1])
toggleModal(session, 'df_popup')
})
observeEvent(input$data, {
rv$selectedRow = input$data
print(paste("selectedRow on 'data':", rv$selectedRow))
})
})
shinyApp(ui, server)
I'm trying to add a "save inputs" feature to my Shiny app where the saved inputs would be saved in a DT data table. If a user clicks an Add button, the inputs would be appended to a data table. A user then can delete a row from this data table by selecting a row and clicking the Delete button. I also need to have this table's values be saved as a global variable so it stays persistent across all sessions.
The example code is shown below. When I close the session, the table (this_table) is correctly updated, however, those changes don't appear realtime during the app. I've tried putting both of these input buttons in an eventReactive function, but this did not work when one of the buttons was selected more than once.
Any ideas?
Global table:
this_table = data.frame(bins = c(30, 50), cb = c(T, F))
Shiny app code:
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30),
checkboxInput("cb", "T/F"),
actionButton("add_btn", "Add"),
actionButton("delete_btn", "Delete")
),
mainPanel(
DTOutput("shiny_table")
)
)
)
server <- function(input, output) {
observeEvent(input$add_btn, {
t = rbind(data.frame(bins = input$bins,
cb = input$cb), this_table)
this_table <<- t
})
observeEvent(input$delete_btn, {
t = this_table
print(nrow(t))
if (!is.null(input$shiny_table_rows_selected)) {
t <- t[-as.numeric(input$shiny_table_rows_selected),]
}
this_table <<- t
})
output$shiny_table <- renderDT({
datatable(this_table, selection = 'single', options = list(dom = 't'))
})
}
shinyApp(ui = ui, server = server)
You can use reactiveVal to add server side variables that are observable and mutable at the same time. The syntax for those variables is to initialize them as
rV <- reactiveValue("init_value")
and update them with
rV("new_value")
Those variables can be accessed inside reactive contexts (basically like inputs) with
rV()
The syntax is quite unusual for R and might take time to get used to, but it is definitely the recommended way to solve issues like these. You might also want to take a look at reactiveValues for a similar functionality but with a semantic closer to the R class list.
Here is how this technique can be applied to your question
library(shiny)
library(DT)
this_table = data.frame(bins = c(30, 50), cb = c(T, F))
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30),
checkboxInput("cb", "T/F"),
actionButton("add_btn", "Add"),
actionButton("delete_btn", "Delete")
),
mainPanel(
DTOutput("shiny_table")
)
)
)
server <- function(input, output) {
this_table <- reactiveVal(this_table)
observeEvent(input$add_btn, {
t = rbind(data.frame(bins = input$bins,
cb = input$cb), this_table())
this_table(t)
})
observeEvent(input$delete_btn, {
t = this_table()
print(nrow(t))
if (!is.null(input$shiny_table_rows_selected)) {
t <- t[-as.numeric(input$shiny_table_rows_selected),]
}
this_table(t)
})
output$shiny_table <- renderDT({
datatable(this_table(), selection = 'single', options = list(dom = 't'))
})
}
shinyApp(ui = ui, server = server)
Finally, I would like to add that # Vishesh Shrivastavs recommendation to use the rhandsontable package is also a viable approach, although you will definitely loose some flexibility in doing so.
I have a small app like this:
require(shiny)
require(shinyjs)
require(rhandsontable)
shinyApp(ui = fluidPage(useShinyjs(),
div(id = 'div1',
titlePanel("RHOT - Form"),
fluidRow(column(width = 3,selectizeInput("Trialid","What Iteration is this?",choices = c('1','2-3','4-7','8-15'))),
column(width = 3,textInput("Techie_Name","Your Name",value='EE')),
column(width = 3,textInput("lab_id","LAB ID",value='NA')),
column(width = 3,textInput("email","Your Email ID",value='eeshanchatterjee#gmail.com'))
),
h4('Observations:'),
rHandsontableOutput("handsontable_obs"),
actionButton("SaveObs", "Save Observations")
),
shinyjs::hidden(div(id = 'SubmitMsg',
h3("Thanks for submitting the Observations!"),
actionLink('addNextObs',"Add Another Observation"))
)
),
server = function(input, output,session){
output$handsontable_obs = renderRHandsontable({
rhandsontable(data.frame(Obs_itr = c(1:5),
Val1 = rep(0,5),
Val2 = rep(0,5)))
})
observeEvent(input$SaveObs,{
shinyjs::reset("div1")
shinyjs::hide("div1")
shinyjs::show("SubmitMsg")
})
observeEvent(input$addNextObs,{
shinyjs::show("div1")
shinyjs::hide("SubmitMsg")
})
}
)
When I run it, I can edit the input fields as well as the tables. Upon hitting the save button, this div resets (using shinyjs::reset), hides, and a hidden thank you div shows up.
Clicking another action link on the 2nd div brings the original one back on.
Now, ass the input fields are reset to their default values, except the handsontable.
Question is, how do I ensure the handsontable resets to default values along with the other input fields?
Adding a reactiveValue and a bit more detail on the rhandsontable gets the job done, but this may not be very efficient:
shinyApp(ui = fluidPage(useShinyjs(),
div(id = 'div1',
titlePanel("RHOT - Form"),
fluidRow(column(width = 3,selectizeInput("Trialid","What Iteration is this?",choices = c('1','2-3','4-7','8-15'))),
column(width = 3,textInput("Techie_Name","Your Name",value='EE')),
column(width = 3,textInput("lab_id","LAB ID",value='NA')),
column(width = 3,textInput("email","Your Email ID",value='eeshanchatterjee#gmail.com'))
),
h4('Observations:'),
rHandsontableOutput("handsontable_obs"),
actionButton("SaveObs", "Save Observations")
),
shinyjs::hidden(div(id = 'SubmitMsg',
h3("Thanks for submitting the Observations!"),
actionLink('addNextObs',"Add Another Observation"))
)
),
server = function(input, output,session){
vals <- reactiveValues(reset=TRUE)
output$handsontable_obs = renderRHandsontable({
input$addNextObs
if(isolate(vals$reset) | is.null(input$handsontable_obs)) {
isolate(vals$reset <- FALSE)
df <- data.frame(Obs_itr = c(1:5),
Val1 = rep(0,5),
Val2 = rep(0,5))
} else df <- hot_to_r(input$handsontable_obs)
rhandsontable(df)
})
observeEvent(input$SaveObs,{
shinyjs::reset("div1")
shinyjs::hide("div1")
shinyjs::show("SubmitMsg")
vals$reset <- TRUE
})
observeEvent(input$addNextObs,{
shinyjs::show("div1")
shinyjs::hide("SubmitMsg")
})
}
)