updating a table with action button - action

I am trying to develop a very basic shiny app. The ui script is very simple
shinyUI(fluidPage(
titlePanel("Drawing a Dice"),
sidebarLayout(
sidebarPanel(
actionButton("action", label = "Draw"),
),
mainPanel(
textOutput("text1")
)
)
))
But i am not sure how to go about doing the server. R
I need the server.R to do the following: Every time the user clicks on draw,it draws a random number from 1:6 and fills the 1st cell of a 10 cell array. And for every click on done till 10 ,it repeats the job. The eventual outcome will be a length 10 vector with random numbers between 1 to 6. In need to give the user an option of exiting by clicking on finish. But i need to be able to retrieve the final resultant vector after closing the app.
Hence the server.R needs to perform the following operation in a one step increments
draw<-function(){
Dice<-c(1:6)
Mydraws<-numeric(10)
for(i in 1:10){
x<-sample(Dice,1,replace=TRUE)
Mydraws[i]=x
}
Mydraws
}
Hence ,i should be able to fetch the Mydraws vector even after user exits by clicking on finish(not included in ui.R)
I do not even know if its possible in shiny.

Here is one way to do it:
server.R
numbers <- list()
shinyServer(function(input, output)
{
output$array <- renderText({
# the presence of input$action will cause this to be evaluated each time the button is clicked
# the value gets incremented each time you click it, which is why we use it as the index of the list
random_number <- sample(1:6,1)
# add to the global variable
numbers[input$action] <<- random_number
return(random_number)
})
})

Related

How do I access the date from an rShiny dateInput?

I am trying to write an input page that takes the date of a section's last training, then calculates the currency of that training (how many days since), but I seem to be having a problem accessing the date from the dateInput element. I'm currently just trying to get it to print, but it is eluding me. Is there something I'm missing, or how can I get this to work? I've commented out the code to (hopefully) calculate the date gap, as I haven't had a date to work through that just yet. If you see an issue there, I'd appreciate that pointer as well.
Thank you!
library(shiny)
ui <- fluidPage(
tags$h3("Section Training"),
dateInput("section_Last_Training",
"When was your last training course?",
daysofweekdisabled = c(0, 6),
max = Sys.Date()
),
)
server <- function(input, output, session) {
section_Last_Training <- reactive({
# dateGap = as.character(difftime(Sys.time(), input$section_Last_Training, units = "days"))
print(input$section_Last_Training)
})
}
shinyApp(ui, server)
It is working, just make sure the last value in reactive is the value you want to assign to the reactive. You can do print, but do it before your gap calculation. Another thing is reactive is "lazily" evaluated. It means if there is no downstream reactivity requires it, it will not be calculated. So you need to add some events that require this reactive to make it work. See the code below.
library(shiny)
ui <- fluidPage(
tags$h3("Section Training"),
dateInput("section_Last_Training",
"When was your last training course?",
daysofweekdisabled = c(0, 6),
max = Sys.Date()
),
)
server <- function(input, output, session) {
section_Last_Training <- reactive({
print(input$section_Last_Training)
as.character(difftime(Sys.time(), input$section_Last_Training, units = "days"))
})
observe(print(section_Last_Training()))
}
shinyApp(ui, server)

How to format R Shiny numericInput?

I have a Shiny app with numerous numericInput fields. I would like a way to format the numericInput fields with commas separating every 10^3. For example, I want 5,000,000 instead of 5000000.
I can do this in R with the format and prettyNum functions. But I don't have a way to do this in Shiny.
This would be very helpful for the UI because it would work with percents, money, etc. Does anyone have any idea how to incorporate this into the numericInput field?
Thanks!
library(shiny)
# Define UI for application that draws a histogram
ui <- fluidPage(
mainPanel(
numericInput("formatNumber",
"Number should be formatted, e.g."5,000,000",
value = 1000),
p(format(5000000.10, big.mark=",", big.interval=3L,
digits=0, scientific=F))
)
)
server <- function(input, output) {
}
shinyApp(ui = ui, server = server)
The shinyWidgets package has a great new function (added as of version 0.5.4, also a disclaimer, I added it via a pull request), autonumericInput that will allow you to do just this. It is based on the javascript library autonumeric. There are a lot of options to the function, but the documentation is extensive and for simple uses most can be ignored.
What you are trying to do can be accomplished as follows:
library(shiny)
library(shinyWidgets)
ui <- fluidPage(
h1("Autonumeric Input Example"),
shinyWidgets::autonumericInput(
inputId = "num",
label = "Enter a large number:",
value = 1000000,
currencySymbolPlacement = "p",
decimalPlaces = 2,
digitGroupSeparator = ",",
decimalCharacter = "."
),
verbatimTextOutput("res1")
)
server <- function(input, output) {
output$res1 <- renderText(input$num)
}
shinyApp(ui = ui, server = server)
This is especially nice because it provides as-you-type formatting, so that the user can easily know how big the number is as they put it in. I know from experience that it is a real pain to try to put large numbers into the base shiny numericInput, trying to count digits in a small little box and figure out how many zeros there are. The goal of this function is to make formatting numeric inputs much easier.
Hopefully this is useful!
I could not find anything that would help with numericInput(), but here's what works with textInput() instead.
library(shiny)
if(interactive()){
shinyApp(
ui <- fluidPage(
mainPanel(
textInput("formatNumber1", "Number should be formatted, e.g.5,000,000", value = 1000),
textInput("formatNumber2", "Number should be formatted, e.g.5,000,000", value = 1000)
)
),
server <- function(input, output, session) {
observe({
updateTextInput(session, "formatNumber1", "Number should be formatted, e.g.5,000,000",
value = prettyNum(input$formatNumber1, big.mark=",", scientific=FALSE))
updateTextInput(session, "formatNumber2", "Number should be formatted, e.g.5,000,000",
value = prettyNum(input$formatNumber2, big.mark=",", scientific=FALSE))
})
}
)
}
That is the only method I found, however if you're too slow or add a digit after the commas have been added, the number is not displayed properly (e.g., 3,000 becomes 3,0,000 if you add a 0 at the end of the string). To correct that, I've changed the updateTextInput() function as below:
updateTextInput(
session,
"formatNumber1",
"Number should be formatted, e.g.5,000,000",
value = prettyNum(
gsub(",", "", input$formatNumber1),
big.mark=",", scientific=FALSE
)
)
In effect gsub() function is used to reset the input to a number every time the input is amended, otherwise the prettyNum() function is only using the digits after the comma and ignoring all digits on the left of the last comma.
If you've got multiple inputs to reformat, then create a function as follows (NB: I've also added req(input[[x]]) to avoid NA appearing when the input is blank):
updatetoprettynb <- function(x) {
req(input[[x]])
updateTextInput(
session,
x,
value = prettyNum(
gsub(",", "", input[[x]]),
big.mark = ",",
scientific = FALSE
)
)
}
You still have to use the function in a similar fashion but don't forget to use "":
observe({
updatetoprettynb("formatNumber1")
})

Updating Shiny UI Element doesn't invalidate its associated input value

I am developing a simple web app using the Interactive Document approach with R_Markdown and Shiny. I have a series of input UI objects that get the user options, each of these modifies the next in a chain, and the final one triggers the output (a chart). So I have
A map, which when clicked defines the
Seasons of data available, when one is selected it gives the
Soil types in the data, when one or more are selected it gives the
Elevation classes in the data, when one is selected we then calculate and draw the
Output chart of the selected data.
So for example there are drop down lists to choose the season and soil(s):
output$season_selector <- renderUI({
cat(file=stderr(), paste("render season selector"), "\n")
selectInput("season", h4("Season?"), my$season_here, selected=my$season_sel)
})
output$soil_selector <- renderUI({
cat(file=stderr(), paste("render soil selector"), "\n")
selectInput("soil", h4("Soils?"), my$soil_here, selected=my$soil_sel,
selectize=FALSE, multiple=TRUE)
})
I observe input$season, and this triggers an update of the "soil" UI element.
observeEvent(input$season, {
...
# reset soil list and default soil selected
my$soil_sel <- my$soil_here
updateSelectInput(session, "soil", choices=my$soil_here, selected=my$soil_sel)
})
However, this does invalidate input$soil (although the UI element options and selection have changed, the user has not made a selection), and so the next step in the cascade doesn't trigger:
observeEvent(input$soil, {
...
# reset elev list and default elev selected
my$soil_sel <- my$soil_here
updateSelectInput(session, "soil", choices=my$soil_here, selected=my$soil_sel)
})
How do I achieve a cascade of reactive UI elements like this? Each reaction needs to trigger either when the UI options and selection changes or when the user makes a different selection. Thanks.
Ok, I think I've fixed it. I needed to use the following pattern, which resets variables further down the chain, and doesn't respond to user inputs unless the value has changed. This avoids duplicate or missing events.
# reset selections
my$soil_default <- my$soil_here
my$soil <- my$soil_default
my$elev <- list(NA)
}) # end reaction to season changing
observeEvent(input$soil, {
req(input$soil, input$soil!="NA")
if (any(my$soil != input$soil)) {
my$soil <- input$soil
my$elev <- list(NA)
}
})
#### react to change of soil ####
observeEvent(my$soil, {
cat(file=stderr(), "\n")
cat(file=stderr(), paste("change of my$soil = "))
cat(file=stderr(), paste(my$soil), "\n")
req(my$soil, my$soil!="NA")

Shiny application -- how to suppress function and rendering on launch?

I developed a Shiny App with RStudio that takes input, performs a search of a look-up table, and then returns a value. The search is not supposed to be performed until the user hits the submit button. However, upon launch, the App automatically performs and returns the first set of values from the lookup table. After this initial search, the app works exactly as expected. Is it possible to suppress this initial search (or have a default value) and perform the search ONLY when the submit button is pressed? I can't present reproducible code, but here is the structure of my input (server.R) and my user-interface (ui.R) code:
#server.R snippet
output$Word <- renderText({
predictWord <- input$gram
predict.function(predictWord) #User-defined function in global.r file
})
#ui.R snippet
tabPanel("Word Prediction",
sidebarPanel(
textInput("gram", "Enter up to three words"),
submitButton("Predict")),
mainPanel(
h4("Word Prediction"),
textOutput("predictWord")))
One way would be to substitute actionButton in place of submitButton and wrap whichever component in an if statement. Here is a simple example to display a number only slightly modified from the Shiny Widget Gallery.
require(shiny)
runApp(list(
ui = pageWithSidebar(
headerPanel("actionButton test"),
sidebarPanel(
numericInput("n", "N:", min = 0, max = 100, value = 50),
br(),
actionButton("goButton", "Go!"),
p("Click the button to update the value displayed in the main panel.")
),
mainPanel(
verbatimTextOutput("nText")
)
),
server = function(input, output){
output$nText <- renderText({
# Take a dependency on input$goButton
if(input$goButton >= 1){
# Use isolate() to avoid dependency on input$n
isolate(input$n)
}
})
}
)
)

Shiny dynamical modalDialog render* difftime

In an attempt to solve this question
R Shiny: display elapsed time while function is running,
I tried several things, and I have questions about the modal dialog.
Here is a MWE
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
actionButton('run', 'Run')
),
mainPanel(
tableOutput("result")
)
)
)
server <- function(input, output) {
N <- 4
rv_time <- reactiveValues(
startTime = Sys.time(),
endTime = Sys.time()
)
output$start <- renderUI({
line1 <- paste("Start at:", format(Sys.time(), format = "%R"))
line2 <- "Be patient, it can takes some time"
HTML(paste(line1, line2, sep = "<br/>"))
})
result_val <- reactiveVal()
observeEvent(input$run,{
showModal(modalDialog(htmlOutput("start"), footer = NULL))
rv_time$startTime <- Sys.time()
result_val(NULL)
for(i in 1:N){
# Long Running Task
Sys.sleep(1)
}
result_val(quantile(rnorm(1000)))
rv_time$endTime <- Sys.time()
# removeModal()
showModal(modalDialog(textOutput("timer"), footer = modalButton("Cancel")))
})
output$result <- renderTable({
result_val()
})
output$timer <- renderText({
paste0("Executed in: ", round(difftime(rv_time$endTime, rv_time$startTime, units = "mins"),2), " minutes")
})
}
shinyApp(ui = ui, server = server)
If you click on the "Run" button the first time, you will see that the first dialog message is empty, the one will the running time works.
If you click another time then on the "Run" button, then everything works. I don't know why this happens.
I could have avoid the call of output$start, and then I would have no problem. But I'd like to understand why it doesn't work, and also, instead of displaying the starting time, I want to display a "dynamical" timer.
Once one clicks on the "Run" button, the dialog box shows the elapsed time since the beginning of the run. So I thought that I need to use an intermediate output$start (I tried to include invalideLater but failed so far). I might be wrong though.
Not related to that, I have a question about difftime. I had to use the option unit = "mins" so I can add the unit behind, because otherwise it doesn't display the unit by default. This example runs in 4 seconds, it would be better than it prints 4 secondes, instead of 0.07 minutes. Is there a way to adapt the unit? (the real code I did runs in several minutes, and possibly hours).
htmlOutput("start") is not calculated when it is hidden. If you add the line
outputOptions(output, "start", suspendWhenHidden = FALSE)
then it will be shown at the first hit on the button.