I'm making an app and I need to add a button to refresh page (same function to press F5). Is there anyone can share a piece of code to implement it?
Thanks a lot!
I do have a very simple and nice solution but it won't work for a file input.
Here's a solution that'll work for all inputs except a file input:
UPDATE 2017: this solution did not work on file inputs for the first 2 years, but it does now.
library(shiny)
library(shinyjs)
runApp(shinyApp(
ui = fluidPage(
shinyjs::useShinyjs(),
div(
id = "form",
textInput("text", "Text", ""),
selectInput("select", "Select", 1:5),
actionButton("refresh", "Refresh")
)
),
server = function(input, output, session) {
observeEvent(input$refresh, {
shinyjs::reset("form")
})
}
))
When you press "Refresh", all inputs will be reset to their initial values. This is what the poster said in a comment that they actually want to do.
But file inputs are very strange and it's hard to "reset" them. See here. You could hack some JavaScript together to try to almost kind of reset an input field if you want.
However, for completeness, you can also refresh the entire page. The easiest way to do that is with session$reload(). You can also do it with {shinyjs}:
library(shiny)
library(shinyjs)
runApp(shinyApp(
ui = fluidPage(
shinyjs::useShinyjs(),
shinyjs::extendShinyjs(text = "shinyjs.refresh_page = function() { location.reload(); }", functions = "refresh_page"),
textInput("text", "Text", ""),
actionButton("refresh", "Refresh")
),
server = function(input, output, session) {
observeEvent(input$refresh, {
shinyjs::js$refresh_page()
})
}
))
Disclaimer: both these solutions use a package I wrote, shinyjs
I have a drop-down list input:
selectInput("domain", label = h4("Domain:"), choices = Domain, selected = CurrentDomain)
The choices set is based on a table in the database. It should change after I add or delete record from the table.
When I was experimenting with your reset or refresh function, the choice set could not reflect the changes and always stay the same. However, when I use the "reload" button provided by the browser, the choice set will update immediately. I am wondering whether you have a reset/refresh solution that is equivalent to the "reload" button of the browser.
I provided my code here, which will not work but will give you an idea what I want to do.
conn<-odbcDriverConnect(connString)
SystemInfo<-sqlQuery(conn, 'SELECT * FROM [DQ].[DQSystemInfo]', stringsAsFactors = FALSE)
close(conn)
Domain<-unique(SystemInfo$Domain)
Domain<-c(Domain,'NEW')
SubDomain<-unique(SystemInfo$SubDomain[SystemInfo$Domain==Domain[1]])
SubDomain<-c(SubDomain,'NEW')
CurrentDomain<-Domain[1]
CurrentSubDomain<-SubDomain[1]
SystemInfo1<-SystemInfo[SystemInfo$Domain==CurrentDomain & SystemInfo$SubDomain==CurrentSubDomain,]
jsResetCode <- "shinyjs.reset = function() {history.go(0)}"
shinyApp(
ui = fluidPage(
shinyjs::useShinyjs(),
shinyjs::extendShinyjs(text = "shinyjs.refresh = function() { location.reload(); }"),
# div(
# id = "form",
fluidRow(
column(6, selectInput("domain", label = h4("Domain:"),
choices = Domain, selected = CurrentDomain)),
column(6,uiOutput("Condition2"))
),
# fluidRow(column(2, verbatimTextOutput("value"))),
fluidRow(
column(6, uiOutput("Condition1")),
column(6,uiOutput("Condition3"))
),
extendShinyjs(text = jsResetCode),
fluidRow(
column(2, actionButton("submit", "Save", class="btn btn-primary btn-lg")),
column(2, actionButton("cancel", "Cancel", class="btn btn-primary btn-
lg")),
column(2, actionButton("delete", "Delete", class="btn btn-primary btn-lg"))
)
#)
),
server = function(input, output) {
observeEvent(input$domain, {
if (input$domain=='NEW') {
shinyjs::disable("domain")
shinyjs::disable("delete")
CurrentSubDomain<-'NEW'
output$Condition1 = renderUI({
textInput("domainT",label = "", value = "")
})
output$Condition3 = renderUI({
textInput("subdomainT", label = "",value = "")
})
})
} else {
CurrentDomain<-input$domain
SubDomain<-unique(SystemInfo$SubDomain[SystemInfo$Domain==input$domain])
SubDomain<-c(SubDomain,'NEW')}
output$Condition2 = renderUI({
selectInput("subdomain", label = h4("SubDomain:"),
choices = SubDomain, selected =CurrentSubDomain)
})
})
observeEvent(input$subdomain, {
if (input$subdomain=='NEW') {
shinyjs::disable("domain")
shinyjs::disable("subdomain")
shinyjs::disable("delete")
output$Condition3 = renderUI({
textInput("subdomainT", label = "", value = "")
})
} else {
CurrentSubDomain<-input$subdomain
conn<-odbcDriverConnect(connString)
SystemInfo<-sqlQuery(conn, 'SELECT * FROM [DQ].[DQSystemInfo]', stringsAsFactors = FALSE)
close(conn)
SystemInfo1<-SystemInfo[SystemInfo$Domain==input$domain & SystemInfo$SubDomain==input$subdomain,]
}
})
observeEvent(input$submit, {
conn<-odbcDriverConnect(connString)
DQ.DQSystemInfo<-SystemInfo[FALSE,c("Domain","SubDomain")]
DQ.DQSystemInfo[1,]<-c("","","","","","","",0,48)
DQ.DQSystemInfo$Domain<-ifelse(input$domain=='NEW',input$domainT,input$domain)
DQ.DQSystemInfo$SubDomain<-input$subdomainT
varType1 <- c("varchar(20)", "varchar(20)" )
names(varType1)<-colnames(DQ.DQSystemInfo)
sqlSave(conn, DQ.DQSystemInfo, append = TRUE, rownames = FALSE, varTypes = varType1)
close(conn)
# js$reset()
#shinyjs::reset("form")
# js$reset("form")
conn<-odbcDriverConnect(connString)
SystemInfo<-sqlQuery(conn, 'SELECT * FROM [DQ].[DQSystemInfo]', stringsAsFactors = FALSE)
close(conn)
Domain<-unique(SystemInfo$Domain)
Domain<-c(Domain,'NEW')
SubDomain<-unique(SystemInfo$SubDomain[SystemInfo$Domain==Domain[1]])
SubDomain<-c(SubDomain,'NEW')
CurrentDomain<-Domain[1]
CurrentSubDomain<-SubDomain[1]
SystemInfo1<-SystemInfo[SystemInfo$Domain==CurrentDomain & SystemInfo$SubDomain==CurrentSubDomain,]
shinyjs::js$refresh()
})
observeEvent(input$cancel, {
#js$reset()
#shinyjs::reset("form")
#js$reset("form")
shinyjs::js$refresh()
})
observeEvent(input$delete, {
conn<-odbcDriverConnect(connString)
delete.query <- paste0("DELETE DQ.DQSystemInfo WHERE Domain='",
input$domain,"' and SubDomain='",input$subdomain,"'")
sqlQuery(conn, delete.query)
close(conn)
#js$reset()
# shinyjs::reset("form")
# js$reset("form")
conn<-odbcDriverConnect(connString)
SystemInfo<-sqlQuery(conn, 'SELECT * FROM [DQ].[DQSystemInfo]', stringsAsFactors = FALSE)
close(conn)
Domain<-unique(SystemInfo$Domain)
Domain<-c(Domain,'NEW')
SubDomain<-unique(SystemInfo$SubDomain[SystemInfo$Domain==Domain[1]])
SubDomain<-c(SubDomain,'NEW')
CurrentDomain<-Domain[1]
CurrentSubDomain<-SubDomain[1]
SystemInfo1<-SystemInfo[SystemInfo$Domain==CurrentDomain & SystemInfo$SubDomain==CurrentSubDomain,]
shinyjs::js$refresh()
})
},options = list(height = 520))
Related
I am new to shiny and am currently trying to develop my first shinyapp.
This apps contains multiple actionButtons and nested observeEvents statements, which I think are the cause of my problem.
The app should allow the user to add observations of species by clicking on a add button, that updates the UI. Within each observation, more details can be asked, but I only showed the species name in the REPREX below (textinput).
Each observation can be deleted individually via a delete button.
Until here, it works! However, I also want a modal dialog to confirm the deletion when the delete button is clicked. To do this, I used a nested observeEvent and it doesn't seem to work (or maybe only for the first time). What am I doing wrong ?
Thanks in advance to anyone who tries to help me.
library(shiny)
library(random)
ui <- fluidPage(
fluidRow(br(), br(), actionButton("adder",
label = "Add an observation"),
align="center")
)
server <- function(input, output,session) {
rv <- reactiveValues()
rv$GridId_list <- c()
observeEvent(input$adder,{
# create random ID for each added species
GridId <- as.character(randomStrings(1, 10))
# store the new ID
rv$GridId_list <- c(rv$GridId_list,GridId)
# ID for the textinput
SpId <- paste(GridId, "sp", sep="_")
# ID of the button used to remove this species
removeSpeciesId <- paste(GridId,'remover', sep="_")
#Update of the UI
insertUI(
selector = '#adder',
where = "beforeBegin",
ui = tags$div(
id = GridId,
fluidRow(
column(6,
h5("Species name : "),
textInput(SpId,label = NULL)
),
column(6, align = "center",
br(),br(),
actionButton(removeSpeciesId,
label = "Delete")
)
)
)
)
# Remove an observation when the "delete" button is clicked (and after confirmation)
observeEvent(input[[removeSpeciesId]], {
#Confirmation modal
showModal(
modalDialog(
"Are you sure ?",
title = "Delete",
footer = tagList(
actionButton("cancel", "Cancel"),
actionButton("confirm", "Confirm", class = "btn btn-danger")
)
)
)
# Delete observation if user confirms
observeEvent(input$confirm, {
id_to_remove <- substring(removeSpeciesId,1, nchar(removeSpeciesId)-8)
rv$GridId_list <- rv$GridId_list[rv$GridId_list!=id_to_remove]
removeUI(selector = paste("#", id_to_remove, sep = ""))
showNotification("Observation deleted !")
removeModal()
})
# Just remove the modal if user cancels
observeEvent(input$cancel, {
removeModal()
})
})
})
}
shinyApp(ui = ui, server = server, options = list(launch.browser = T))
Referencing dynamic input id's is a pain. I find it best to add a last clicked input identifier to reference. You can add a class to those inputs to just listen to them and not others in your app:
tags$head(tags$script(HTML("$(document).on('click', '.needed', function () {
Shiny.onInputChange('last_btn',this.id);
});")))
That little piece of code will allow you to get an input$last_btn id, that you can use for your event listeners. In this case you don't need to nest your event listeners; it is better to think about the events in sequence and program those reactions. So, with some tweakings in your code, your app now looks like this:
library(shiny)
library(random)
ui <- fluidPage(
tags$head(tags$script(HTML("$(document).on('click', '.needed', function () {
Shiny.onInputChange('last_btn',this.id);
});"))),
fluidRow(br(), br(), actionButton("adder",
label = "Add an observation"),
align="center")
)
server <- function(input, output,session) {
rv <- reactiveValues()
rv$GridId_list <- c()
observeEvent(input$adder,{
# create random ID for each added species
GridId <- as.character(randomStrings(1, 10))
# store the new ID
rv$GridId_list <- c(rv$GridId_list,GridId)
# ID for the textinput
SpId <- paste(GridId, "sp", sep="_")
# ID of the button used to remove this species
removeSpeciesId <- paste(GridId,'remover', sep="_")
#Update of the UI
insertUI(
selector = '#adder',
where = "beforeBegin",
ui = tags$div(
id = GridId,
fluidRow(
column(6,
h5("Species name : "),
textInput(SpId,label = NULL)
),
column(6, align = "center",
br(),br(),
actionButton(removeSpeciesId,
label = "Delete", class="needed")
)
)
)
)
})
# Remove an observation when the "delete" button is clicked (and after confirmation)
observeEvent(input$last_btn, {
observeEvent(input[[input$last_btn]] > 0,{#We want the modal to show when any "remover" id is clicked
#Confirmation modal
showModal(
modalDialog(
"Are you sure ?",
title = "Delete",
footer = tagList(
actionButton("cancel", "Cancel"),
actionButton("confirm", "Confirm", class = "btn btn-danger")
)
)
)
})
}, ignoreNULL = TRUE, ignoreInit = TRUE)
# Delete observation if user confirms
observeEvent(input$confirm, {
#The following selector is for the parent id of the parent id of the last_btn id
removeUI(selector = paste0("div:has(>div:has(>#", input$last_btn, "))"))
showNotification("Observation deleted !")
removeModal()
})
# Just remove the modal if user cancels
observeEvent(input$cancel, {
removeModal()
})
}
shinyApp(ui = ui, server = server, options = list(launch.browser = T))
I recently asked a similar question (Shiny DT datatable input reactivity after table is reloaded). My issue was getting a selectInput in a DT datatable to work correctly after the table is reloaded. The solution worked, which was to use javascript to unbind before reloading the table. However, that example used a static dataframe. When the input data in the datatable are reactive, it doesn't work. In the example below, when the user clicks "Update data" the first time to load data, the selectInput works correctly and input$id1 responds to the user selection. However, when the user clicks "Update data" again to update the reactive data, the input$id no longer responds to the user selection. I've seen two potential approaches to address the issue. One is using dataTableProxy() and replaceData(), and the other is renaming the selectInput ids each time the reactive data are updated. I was wondering if I can avoid those two approaches and get this example working with minimal changes.
require(shiny)
require(DT)
shinyApp(
ui = fluidPage(
tags$head(tags$script(
HTML(
"Shiny.addCustomMessageHandler('unbindDT', function(id) {
var $table = $('#'+id).find('table');
if($table.length > 0){
Shiny.unbindAll($table.DataTable().table().node());
}
})")
)),
actionButton(inputId = "update", label = "Update data"),
uiOutput("resettable_table")
),
server = function(input, output, session) {
rv <- reactiveValues(
times = 1,
mydata = NULL
)
observeEvent(input$update, {
session$sendCustomMessage("unbindDT", "mytable")
rv$times <- rv$times + 1
rv$mydata <- data.frame(
Col1 = as.character(selectInput(
inputId = "id1",
label = NULL,
choices = paste0(letters, input$update),
selected = paste0(letters, input$update)[1],
))
)
})
output$mytable <- DT::renderDataTable({
req(rv$mydata)
DT::datatable(
data = rv$mydata,
escape = F,
selection = "none",
options = list(
preDrawCallback = JS('function(){Shiny.unbindAll(this.api().table().node());}'),
drawCallback = JS('function(){Shiny.bindAll(this.api().table().node());}')
)
)
}, server = F)
output$resettable_table <- renderUI({
req(rv$times)
div(
id = paste0("mydiv", rv$times),
DT::dataTableOutput("mytable")
)
})
observe({
if(is.null(input$id1)) {
print("input$id1 is NULL")
} else {
print(paste(c("input$id1:", input$id1)))
}
})
}
)
Update
Thanks to #StephaneLaurent for pointing out that the reactive counter keeping track of the number of times the data were reloaded was causing the issue. It wasn't actually necessary to put the DT datatable inside a div with an id that updated each time. Here is working code:
require(shiny)
require(DT)
shinyApp(
ui = fluidPage(
tags$head(tags$script(
HTML(
"Shiny.addCustomMessageHandler('unbindDT', function(id) {
var $table = $('#'+id).find('table');
if($table.length > 0){
Shiny.unbindAll($table.DataTable().table().node());
}
})")
)),
actionButton(inputId = "update", label = "Update data"),
DT::dataTableOutput("mytable")
),
server = function(input, output, session) {
rv <- reactiveValues(mydata = NULL)
observeEvent(input$update, {
session$sendCustomMessage("unbindDT", "mytable")
rv$mydata <- data.frame(
Col1 = as.character(selectInput(
inputId = "id1",
label = NULL,
choices = paste0(letters, input$update),
selected = paste0(letters, input$update)[1],
))
)
})
output$mytable <- DT::renderDataTable({
req(rv$mydata)
DT::datatable(
data = rv$mydata,
escape = F,
selection = "none",
options = list(
preDrawCallback = JS('function(){Shiny.unbindAll(this.api().table().node());}'),
drawCallback = JS('function(){Shiny.bindAll(this.api().table().node());}')
)
)
}, server = F)
observe({
if(is.null(input$id1)) {
print("input$id1 is NULL")
} else {
print(paste(c("input$id1:", input$id1)))
}
})
}
)
The problem is caused by the presence of rv$times in the renderUI. The simplest way to make this app work is to get rid of this renderUI.
However, for fun, and in order to understand what happens, I did the app below which works with the renderUI and which shows what happens. The key point was to remove the id1 element when the table is consecutively rendered two times, before the second rendering. To do so, I use a JavaScript counter i.
library(shiny)
library(DT)
shinyApp(
ui = fluidPage(
tags$head(tags$script(
HTML("var i = 1;")
)),
actionButton(inputId = "update", label = "Update data"),
uiOutput("resettable_table")
),
server = function(input, output, session) {
rv <- reactiveValues(
times = 1,
mydata = NULL
)
observeEvent(input$update, {
rv$times <- rv$times + 1
rv$mydata <- data.frame(
Col1 = as.character(selectInput(
inputId = "id1",
label = NULL,
choices = paste0(letters, input$update),
selected = paste0(letters, input$update)[1],
))
)
})
output$mytable <- DT::renderDataTable({
req(rv$mydata)
DT::datatable(
data = rv$mydata,
escape = F,
selection = "none",
options = list(
initComplete = JS('function(settings) { alert("initComplete - incrementing i"); i++; alert("i = " + i)}'),
preDrawCallback = JS('function() { alert("preDrawCallback triggered - unbinding"); Shiny.unbindAll(this.api().table().node()); }'),
drawCallback = JS('function() { alert("drawCallback triggered - i = " + i); if(i===2) {alert("removing id1 and resetting i to 0"); $("#id1").remove(); i=0;} Shiny.bindAll(this.api().table().node());}')
)
)
}, server = F)
output$resettable_table <- renderUI({
div(
id = paste0("mydiv", rv$times),
tags$p(paste0("mydiv", rv$times)),
DT::dataTableOutput("mytable")
)
})
observe({
if(is.null(input$id1)) {
print("input$id1 is NULL")
} else {
print(paste(c("input$id1:", input$id1)))
}
})
}
)
I want to make an app with 2 actionButtons: 1) to submit the changes before loading a selectizeInput and 2) to draw the plot.
I know how to add a spinner after clicking a actionButton but the majority of the cases is added when you want to show the plot.
However, is it possible to add a spinner without showing any plot?
In this particular case, I want to show a spinner after clicking "Submit" until the selectizeInput from the 'Selection tab' is loaded. As you can see the example that I attach, it takes a bit to load all the choices (since the file has 25000 rows).
I already have one spinner after clicking the second actionButton (Show the plot) but I need one more.
I have created an example, but for some reason the plot is not shown in the shiny app and it appears in the window from R (I don't know why but I added the plot just to show you how I put the second spinner. I want a similar one but with the first actionButton.).
library(shiny)
library(shinycssloaders)
ui <- fluidPage(
titlePanel("My app"),
sidebarLayout(
sidebarPanel(
tabsetPanel(
tabPanel("Submit",
checkboxInput("log2", "Log2 transformation", value = FALSE),
actionButton("submit", "Submit")
),
tabPanel("Selection",
br(),
selectizeInput(inputId = "numbers", label = "Choose one number:", choices=character(0)),
actionButton("show_plot", "Show the plot")
))
),
mainPanel(
conditionalPanel(
condition = "input.show_plot > 0",
style = "display: none;",
withSpinner( plotOutput("hist"),
type = 5, color = "#0dc5c1", size = 1))
)
)
)
server <- function(input, output, session) {
data <- reactive({
data = read.csv("https://people.sc.fsu.edu/~jburkardt/data/csv/hw_25000.csv")
data[,1] <- as.character(data[,1])
if(input$log2 == TRUE){
cols <- sapply(data, is.numeric)
data[cols] <- lapply(data[cols], function(x) log2(x+1))
}
return(data)
})
mylist <- reactive({
req(data())
data <- data()
data <- data[,1]
return(data)
})
# This is to generate the choices (gene list) depending on the user's input.
observeEvent(input$submit, {
updateSelectizeInput(
session = session,
inputId = "numbers",
choices = mylist(), options=list(maxOptions = length(mylist()))
)
})
v <- reactiveValues()
observeEvent(input$show_plot, {
data <- data()
v$plot <- plot(x=data[,1], y=data[,2])
})
# If the user didn't choose to see the plot, it won't appear.
output$hist <- renderPlot({
req(data())
if (is.null(v$plot)) return()
if(input$show_plot > 0){
v$plot
}
})
}
Does anyone know how to help me, please?
Thanks very much
It's a little tricky.
First of all I'd update the selectizeInput on the server side as the warning suggests:
Warning: The select input "numbers" contains a large number of
options; consider using server-side selectize for massively improved
performance. See the Details section of the ?selectizeInput help
topic.
Furthermore I switched to ggplot2 regarding the plotOutput - Please see this related post.
To show the spinner while the selectizeInput is updating choices we'll need to know how long the update takes. This information can be gathered via shiny's JS events - please also see this article.
Finally, we can show the spinner for a non-existent output, so we are able to control for how long the spinner is shown (see uiOutput("dummyid")):
library(shiny)
library(shinycssloaders)
library(ggplot2)
ui <- fluidPage(
titlePanel("My app"),
tags$script(HTML(
"
$(document).on('shiny:inputchanged', function(event) {
if (event.target.id === 'numbers') {
Shiny.setInputValue('selectizeupdate', true, {priority: 'event'});
}
});
$(document).on('shiny:updateinput', function(event) {
if (event.target.id === 'numbers') {
Shiny.setInputValue('selectizeupdate', false, {priority: 'event'});
}
});
"
)),
sidebarLayout(
sidebarPanel(
tabsetPanel(
tabPanel("Submit",
checkboxInput("log2", "Log2 transformation", value = FALSE),
actionButton("submit", "Submit")
),
tabPanel("Selection",
br(),
selectizeInput(inputId = "numbers", label = "Choose one number:", choices=NULL),
actionButton("show_plot", "Show the plot")
))
),
mainPanel(
uiOutput("plotProxy")
)
)
)
server <- function(input, output, session) {
previousEvent <- reactiveVal(FALSE)
choicesReady <- reactiveVal(FALSE)
submittingData <- reactiveVal(FALSE)
observeEvent(input$selectizeupdate, {
if(previousEvent() && input$selectizeupdate){
choicesReady(TRUE)
submittingData(FALSE)
} else {
choicesReady(FALSE)
}
previousEvent(input$selectizeupdate)
})
data <- reactive({
data = read.csv("https://people.sc.fsu.edu/~jburkardt/data/csv/hw_25000.csv")
if(input$log2 == TRUE){
cols <- sapply(data, is.numeric)
data[cols] <- lapply(data[cols], function(x) log2(x+1))
}
return(data)
})
mylist <- reactive({
req(data()[,1])
})
observeEvent(input$submit, {
submittingData(TRUE)
reactivePlotObject(NULL) # reset
updateSelectizeInput(
session = session,
inputId = "numbers",
choices = mylist(), options=list(maxOptions = length(mylist())),
server = TRUE
)
})
reactivePlotObject <- reactiveVal(NULL)
observeEvent(input$show_plot, {
reactivePlotObject(ggplot(data(), aes_string(x = names(data())[1], y = names(data())[2])) + geom_point())
})
output$hist <- renderPlot({
reactivePlotObject()
})
output$plotProxy <- renderUI({
if(submittingData() && !choicesReady()){
withSpinner(uiOutput("dummyid"), type = 5, color = "#0dc5c1", size = 1)
} else {
conditionalPanel(condition = "input.show_plot > 0", withSpinner(plotOutput("hist"), type = 5, color = "#0dc5c1", size = 1), style = "display: none;")
}
})
}
shinyApp(ui, server)
First 100 rows of your example data (dput(head(data, 100)) - your link might be offline some day):
structure(list(Index = 1:100, Height.Inches. = c(65.78331, 71.51521,
69.39874, 68.2166, 67.78781, 68.69784, 69.80204, 70.01472, 67.90265,
66.78236, 66.48769, 67.62333, 68.30248, 67.11656, 68.27967, 71.0916,
66.461, 68.64927, 71.23033, 67.13118, 67.83379, 68.87881, 63.48115,
68.42187, 67.62804, 67.20864, 70.84235, 67.49434, 66.53401, 65.44098,
69.5233, 65.8132, 67.8163, 70.59505, 71.80484, 69.20613, 66.80368,
67.65893, 67.80701, 64.04535, 68.57463, 65.18357, 69.65814, 67.96731,
65.98088, 68.67249, 66.88088, 67.69868, 69.82117, 69.08817, 69.91479,
67.33182, 70.26939, 69.10344, 65.38356, 70.18447, 70.40617, 66.54376,
66.36418, 67.537, 66.50418, 68.99958, 68.30355, 67.01255, 70.80592,
68.21951, 69.05914, 67.73103, 67.21568, 67.36763, 65.27033, 70.84278,
69.92442, 64.28508, 68.2452, 66.35708, 68.36275, 65.4769, 69.71947,
67.72554, 68.63941, 66.78405, 70.05147, 66.27848, 69.20198, 69.13481,
67.36436, 70.09297, 70.1766, 68.22556, 68.12932, 70.24256, 71.48752,
69.20477, 70.06306, 70.55703, 66.28644, 63.42577, 66.76711, 68.88741
), Weight.Pounds. = c(112.9925, 136.4873, 153.0269, 142.3354,
144.2971, 123.3024, 141.4947, 136.4623, 112.3723, 120.6672, 127.4516,
114.143, 125.6107, 122.4618, 116.0866, 139.9975, 129.5023, 142.9733,
137.9025, 124.0449, 141.2807, 143.5392, 97.90191, 129.5027, 141.8501,
129.7244, 142.4235, 131.5502, 108.3324, 113.8922, 103.3016, 120.7536,
125.7886, 136.2225, 140.1015, 128.7487, 141.7994, 121.2319, 131.3478,
106.7115, 124.3598, 124.8591, 139.6711, 137.3696, 106.4499, 128.7639,
145.6837, 116.819, 143.6215, 134.9325, 147.0219, 126.3285, 125.4839,
115.7084, 123.4892, 147.8926, 155.8987, 128.0742, 119.3701, 133.8148,
128.7325, 137.5453, 129.7604, 128.824, 135.3165, 109.6113, 142.4684,
132.749, 103.5275, 124.7299, 129.3137, 134.0175, 140.3969, 102.8351,
128.5214, 120.2991, 138.6036, 132.9574, 115.6233, 122.524, 134.6254,
121.8986, 155.3767, 128.9418, 129.1013, 139.4733, 140.8901, 131.5916,
121.1232, 131.5127, 136.5479, 141.4896, 140.6104, 112.1413, 133.457,
131.8001, 120.0285, 123.0972, 128.1432, 115.4759)), row.names = c(NA,
100L), class = "data.frame")
I have two tables and I'm trying to show one at a time based on user input in radio buttons. If the input from the radio buttons is "table", i'd like to show table1. If the input is else i'd like to show table2.
observeEvent(input$visuBtn,{
req(input$visuBtn)
print(input$visubtn)
if(input$visuBtn == "table"){
hide("table2")
#DT::dataTableOutput("table1")
renderUI(
DT::dataTableOutput("table1")
)
}else{
print("Should show table2")
# removeUI(
# selector = "table"
# )
renderUI(
DT::dataTableOutput("table2")
)
#DT::dataTableOutput("table2")
#show("table2")
}
})
I've tried doing this by showing and hiding the two tables and can't figure out how to get that to work. I"ve also tried using renderUI as well. What would be the best methodology to go about this?
mainPanel(
tabsetPanel(id = "sim.tabset",
tabPanel(title = "Results",
# tableOutput("table")
DT::dataTableOutput("table"),
DT::dataTableOutput("table2")
),
)
Depending on your app, you can toggle the visibility of the table in the frontend with a little bit of javascript. In the UI, create a button and wrap the dataTableOutput in a generic container.
# some where in your UI
actionButton("toggleTable", "Toggle Table"),
tags$div(
id = "tableContainer",
DT::dataTableOutput("table")
)
...
There are many ways to toggle the visibility of an element (changing the display properties, toggling css classes, modifying other attributes, etc.). The following function toggles the html attribute hidden when the button is clicked. This can be defined in the UI using the tags$script function or loaded from an external javascript file.
const btn = document.getElementById('toggle');
const elem = document.getElementById('tableContainer');
btn.addEventListener('click', function(event) {
if (elem.hasAttribute('hidden')) {
elem.removeAttribute('hidden');
} else {
elem.setAttribute('hidden', '');
}
});
In the server, render the datatable as normal and you can remove the toggling (unless you need additional things to happen when the button is clicked).
Here is the full example.
library(shiny)
shinyApp(
ui = tagList(
tags$main(
id = "main",
tags$h1("Collapsible Table Example"),
actionButton("toggleTable", "Toggle Table"),
tags$div(
id = "tableContainer",
DT::dataTableOutput("table")
)
),
tags$script(
type = "text/javascript",
"
const btn = document.getElementById('toggleTable');
const elem = document.getElementById('tableContainer');
btn.addEventListener('click', function(event) {
if (elem.hasAttribute('hidden')) {
elem.removeAttribute('hidden');
} else {
elem.setAttribute('hidden', '');
}
});
"
)
),
server = function(input, output, session) {
output$table <- DT::renderDataTable({
data.frame(
group = sample(c("A", "B"), 20, replace = TRUE),
x = rnorm(n = 20, mean = 50, sd = 2),
y = rnorm(n = 20, mean = 50, sd = 2)
)
})
}
)
I opted to go with a simple solution, just having one table that renders based on the choice of the radiobuttons. Meaning the if/else is just within the renderDataTable function
library(shiny)
library(DT)
ui <- fluidPage(
radioButtons("Buttons", "CHOOSE!", choices = c("MTCARS", "IRIS")),
DT::dataTableOutput("THETABLE")
)
server <- function(input, output, session) {
output$THETABLE<-DT::renderDataTable({
req(input$Buttons)
if(input$Buttons == "MTCARS") {
DT::datatable(mtcars)
} else {
DT::datatable(iris)
}
})
}
shinyApp(ui, server)
Alternatively, you could use conditional panel, so it shows the table based on the radiobutton selection:
library(shiny)
library(DT)
ui <- fluidPage(
radioButtons("Buttons", "CHOOSE!", choices = c("MTCARS", "IRIS")),
conditionalPanel("input.Buttons == 'MTCARS'",
DT::dataTableOutput("TABLEMTCARS")
),
conditionalPanel("input.Buttons == 'IRIS'",
DT::dataTableOutput("TABLEIRIS"))
)
server <- function(input, output, session) {
output$TABLEMTCARS<-DT::renderDataTable({
DT::datatable(mtcars)
})
output$TABLEIRIS<-DT::renderDataTable({
DT::datatable(iris)
})
}
shinyApp(ui, server)
I'm trying to build an interactive UI by rendering it inside an output with renderUI. The thing is: I have inputs created inside this rendering function whose behavior should change according to the answers provided. But when I do it, the reactivity updates the entire output and erases the answers provided, resetting the inputs to original state. Is there a way to determine which inputs I want to update? Or is there a better way of building this structure?
EDIT: Just to clarify: I want to change the label of the textInput without updating the radioButtons. The second radioButton answer should affect the behaviour of the textInput only.
ui <- miniPage(
miniTabstripPanel(id = 'tabs',
miniTabPanel("Data",
miniContentPanel(
selectInput(inputId = 'indicator', label = "Select indicator:",
choices = c('Select an indicator' = 'none',
"Water" = 'iwater',
'Antenatal care 4+ visits' = 'anc4',
'Institutional delivery' = 'ideliv')),
)
),
miniTabPanel("Second tab",
miniContentPanel(
uiOutput(outputId = "indicarea")
)
)
)
)
server <- function(input, output, session) {
iwater_vartype = reactiveVal(value= "Example label 1")
observeEvent(input$iwater_variabletype,{
if (input$iwater_variabletype == 'codes') {
iwater_vartype("Example label 1")
}
else {
iwater_vartype("Example label 2")
}
})
observeEvent(input$indicator,{
output$indicarea = renderUI({
buildUI(input$indicator)
})
})
buildUI = function(indic) {
switch(indic,
'none' = {
h3("Please select an indicator to proceed.")
},
'iwater' = {
tagList(
h3("Improved source of drinking water"),
br(), hr(), br(),
radioButtons(inputId = 'iwater_subsample', label = "Asked it in all?",
choices = c('Yes' = 'yes', 'No' = 'no')),
radioButtons(inputId = 'iwater_variabletype', label = "How was the info collected?",
choices = c('One variable' = 'codes', 'Several variables' = 'variables')),
textInput(inputId = 'iwater_sourcevariable', label= iwater_vartype())
)
},
'anc4' = {
tagList(
textInput(inputId = 'test', label= 'testing')
)
}
)
}
}
runGadget(ui, server)
Thanks in advance!
you could try adding an observeEvent to you server code with something like this.
observeEvent(input$iwater_variabletype,{
updateTextInput(
session = session,
inputId = "iwater_sourcevariable",
label = "new Label for text Input"
)
})
hope this helps!