Alternative to accessing reactiveValues in Shiny UI? - r

I want to create a dynamic UI in Shiny, where each time a button is clicked, a new UI element is created with several input fields. I was hoping that I could do this using reactiveValues, however the ui code can't access them, so I can't tell it how many elements to show.
Here's a reproducible example with just a single UI field created on each click - it works for the first two clicks of the button, but since the lapply in the ui section is coded to a fixed value (3 in this example), after that the new ones stop being displayed. I know I could set the ui value at a higher number, but what I'd like is for it to be reactive. (In the full version I'd like to have nested elements within each of these that work the same way, and buttons to remove each field as well.)
server <- function(input, output) {
rv <- reactiveValues(numFields = 1)
#
# start with one input box
#
output$textUI1 <- renderUI(textInput("textInput1", "Input #1"))
#
# each time the button is clicked, increase the reactive value
#
observeEvent(input$addField, rv$numFields <- rv$numFields + 1)
#
# render any additional UI input fields according to value of rv$numFields
#
observe({
if(rv$numFields > 1)
{
lapply(2:rv$numFields, function(i) {
output[[paste0("textUI", i)]] <- renderUI({
textInput(paste0("textInput", i), paste0("Input #", i))
})
})
}
})
}
ui <- fluidPage(sidebarLayout(
sidebarPanel(
actionButton("addField", "Add text input box")
),
mainPanel(
# UI output
lapply(1:3, function(i) { # instead of 3 I want something like rv$numFields here
uiOutput(paste0("textUI", i))
})
)
))
shinyApp(ui, server)

Instead of passing the variable from server to ui why don't you create the whole dynamic ui inside your server. Something like this:
library (shiny)
server <- function(input, output) {
rv <- reactiveValues(numFields = 1)
#
# start with one input box
#
output$textUI <- renderUI(textInput("textInput1", "Input #1"))
#
# each time the button is clicked, increase the reactive value and add a new text input
observeEvent(input$addField,{
rv$numFields <- rv$numFields + 1
output$textUI <- renderUI({
lapply(1:rv$numFields, function(i) {textInput(paste0("textInput", i), paste0("Input #", i))
})
})
})
}
ui <- fluidPage(sidebarLayout(
sidebarPanel(
actionButton("addField", "Add text input box")
),
mainPanel(
uiOutput("textUI")
)
))
shinyApp(ui, server)

Related

How to reactively and repeatedly render the same type of object with an action button in R shiny?

The code at the bottom is taken from an example in https://shiny.rstudio.com/articles/modules.html though I de-modularized it so I can understand something more basic. With this code, each click of the action button renders a counter which counts the number of clicks. Fine.
Instead of counting the number of clicks in the same output of verbatimTextOutput() as the code currently works, I'd like each click to be represented as a new output of verbatimTextOutput(). See illustration below which shows what I'm trying to derive, assuming the user clicks the action button 3 times. I don't know how many times the user will click the action button so there's no way to pre-set or hard-code the number of outputs and assign output names such as output$out1, output$output2, etc. Is there a way to reactively generate the outputs names, as a I naively attempted in the below code with output$"paste(out,count())" and verbatimTextOutput("paste(out,count())") (without the quote marks, I only put them in so the example code would work)? If this is possible this could be a way to achieve the results I am seeking.
Illustration:
Code:
library(shiny)
newOutput <- function(x,y){verbatimTextOutput("paste(out,count())")}
ui <- fluidPage(uiOutput("uiButton"))
server <- function(input,output,session){
count <- reactiveVal(0)
observeEvent(input$button, {count(count() + 1)})
output$"paste(out,count())" <- renderText({count()})
count
output$uiButton <-
renderUI(
tagList(
actionButton("button", label = "Click me"),
newOutput()
)
)
}
shinyApp(ui, server)
This is an alternative approach using insertUI.
Compared to #stefan's renderUI based solution it has the advantage, that the UI elements are rendered only once. Using renderUI every element is re-rendered on button click, accordingly things will slow down depending on the number of elements.
library(shiny)
ui <- fluidPage(
actionButton("add", "Add UI")
)
server <- function(input, output, session) {
observeEvent(input$add, {
output_name <- paste0("out_", input$add)
output[[output_name]] <- renderText({
isolate(input$add)
})
insertUI(
selector = ifelse(input$add == 0L, "#add", paste0("#", "out_", input$add-1)),
where = "afterEnd",
ui = verbatimTextOutput(output_name)
)
}, ignoreNULL = FALSE)
}
shinyApp(ui, server)
Also check ?removeUI.
Adapting this example to dynamically create graphs to your example you could do:
library(shiny)
library(purrr)
newOutput <- function(x) {
verbatimTextOutput(x)
}
ui <- fluidPage(
actionButton("button", label = "Click me"),
uiOutput("uiText")
)
server <- function(input, output, session) {
count <- reactiveVal(0)
observeEvent(input$button, {
count(count() + 1)
i <- count()
output_name <- paste("out", i)
output[[output_name]] <- renderText({
i
})
})
output$uiText <- renderUI({
out_list <- map(seq_len(count()), ~ {
tagList(
newOutput(paste("out", .x))
)
})
tagList(out_list)
})
}
shinyApp(ui, server)

Refer to the Updated UI Input ID and Calculate the Sum in Shiny

I would like to design a Shiny app with two buttons. Users can click the "Add UI" button as many times as they want, which will return text boxes. Users can then type numbers to the input boxes, click the "Sum" button, and calculate the total.
Below is my current code, modified from the sample code from ?insertUI. My question is I am not sure how to refer to the input id from the updated UI (in this case, the new text boxes). My current attempt cannot calculate the sum. The end result is always 0.
# Define UI
ui <- fluidPage(
actionButton("add", "Add UI"),
actionButton("sum", "Sum"),
# Report the output
h4("The total from input"),
textOutput("text")
)
# Server logic
server <- function(input, output, session) {
observeEvent(input$add, {
insertUI(
selector = "#add",
where = "afterEnd",
ui = textInput(paste0("txt", input$add),
"Insert some text")
)
})
# Calculate the total from the text inputs
output$text <- eventReactive(input$sum, {
as.character(sum(as.numeric(unlist(mget(ls(pattern = "^txt"))))))
})
}
# Complete app with UI and server components
shinyApp(ui, server)
You can use the special Shiny variable input to check and access the current inputs (and values) in your app. Thus you can get at newly inserted UI elements (assuming they all follow a pattern) and compute against them.
output$text <- eventReactive(input$sum, {
txt_inpt_names <- names(input)[grepl("^txt", names(input))]
sum(sapply(txt_inpt_names, function(x) as.numeric(input[[x]])), na.rm = T)
})
Worth noting that Shiny requires single (one-at-a-time) access to input values so thats why sapply() is required and not just input[[txt_inpt_names]].

action/submit button for multiple numeric inputs in shiny

In the shiny application multiple numeric input widgets are generated dynamically each having an initial value inside which are row-column numbers. The sum of those values are also displayed in main panel.
As the user changes the numeric input values the sum updates accordingly. Currently it is instantly. I want to delay the process of inputs in main panel for all numeric inputs by adding action/submit button till I change more than one numeric Inputs.
But I am getting the following error if action/submit buttons are used inside the render functions.
Warning: Error in <<-: number of items to replace is not a multiple of replacement length.
If not inside render functions where else to place as these widgets are generated inside renderUI only.
library(shiny)
ui <- shinyUI(fluidPage(
titlePanel(title = "Use of action/submit button for multiple inputs"),
sidebarLayout(
sidebarPanel(numericInput("rows","Input No. of rows",value = 3,min=1),
br(),
numericInput("col","Input No. of cols",value = 1,min=1)),
mainPanel(textOutput("display"),
uiOutput("plo")
))))
Server.r
server <- function(input,output){
# creating input widgets dynamically
output$plo <- renderUI({
z <- input$col
lapply(seq(input$col), function(j){
column(width=3,
lapply(seq(input$rows),function(i){
numericInput(inputId = paste0("range",paste0(i,j)),label = j,value = paste0(i,j))
})
)
})
})
# capturing the value of input widgets in a matrix
cm <- reactive({
c <- input$col
r <- input$rows
changed_m <- matrix(nrow = r,ncol = c)
lapply(seq(input$col), function(j){
lapply(seq(input$rows),function(i){
changed_m[i,j] <<- input[[paste0("range",paste0(i,j))]]
})
})
changed_m
})
# display the sum
output$display <- renderText({
paste0("Sum of matrix: ",sum(cm()))
})
}
Here is a working example of a possible solution. You can store the string to display (or just the sum of course) in a reactiveVal, and update this only when the user clicks the button, or display an alternative text when one of the inputs has changed so the user knows the sum is no longer correct.
Hope this helps!
library(shiny)
ui <- shinyUI(fluidPage(
titlePanel(title = "Use of action/submit button for multiple inputs"),
sidebarLayout(
sidebarPanel(numericInput("rows","Input No. of rows",value = 3,min=1),
br(),
numericInput("col","Input No. of cols",value = 1,min=1),
actionButton('update' ,'update!')),
mainPanel(textOutput("display"),
uiOutput("plo")
))))
server <- function(input,output){
# creating input widgets dynamically
output$plo <- renderUI({
z <- input$col
lapply(seq(input$col), function(j){
column(width=3,
lapply(seq(input$rows),function(i){
numericInput(inputId = paste0("range",paste0(i,j)),label = j,value = paste0(i,j))
})
)
})
})
# capturing the value of input widgets in a matrix
cm <- reactive({
c <- input$col
r <- input$rows
changed_m <- matrix(nrow = r,ncol = c)
lapply(seq(input$col), function(j){
lapply(seq(input$rows),function(i){
x=input[[paste0("range",paste0(i,j))]]
changed_m[i,j] <<- ifelse(!is.null(x),x,0)
})
})
changed_m
})
# initialize our reactiveVal with an empty string
my_sum <- reactiveVal('')
# observer that listens to the button click, then updates the sum string.
observeEvent(input$update,{
my_sum(paste0("Sum of matrix: ",sum(cm())))
})
# observer that listens to changes in the input, then updates the sum string.
observeEvent(cm(),ignoreNULL = T,ignoreInit = T, {
isolate(my_sum('invalidated. Press button to update.'))
})
# display the sum string
output$display <- renderText({
my_sum()
})
}
shinyApp(ui,server)

Displaying an updated variable after clicking an action button in Shiny

I'm new to shiny and trying to accomplish rather a simple task using an action button:
User clicks a button and a function is called
This function does some calculations using input variables and updates/creates several global variables (reactiveValues, probably inside an observe block?)
I'd like to display those values back on the UI (using render* function)
Whenever user changes input values, the UI is automatically updated
Relevant code bits are:
server.R
...
rv <- reactiveValues()
observe({
if(input$run){
rv$a <- someFunc(input$aa)
}
})
output$msg = renderText({ rv$a })
...
ui.R
...
selectInput("aa", ...)
...
actionButton("run", "Run")
...
textOutput("msg")
How can I change msg based on the input aa each time user clicks the button?
I am not convinced I understood what you want, but I imagine it to be something like this:
library(shiny)
u <- fluidPage(
titlePanel("Simple Selectable Reactive Function"),
sidebarLayout(
sidebarPanel(
sliderInput("vv", "Choose a value",min=-3.14,max=3.14,value=0),
selectInput("aa", "Choose a function", choices=c("sin","cos","exp")),
actionButton("run", "Change Function and Run")
),
mainPanel(
h2("Results"),
verbatimTextOutput("msg")
)))
s <- function(input,output){
rv <- reactiveValues(func=NULL)
observeEvent(input$run,{ rv$func <- input$aa })
funcval <- reactive({
v <- 0
if (rv$func=="sin") v <- sin(input$vv)
if (rv$func=="cos") v <- cos(input$vv)
if (rv$func=="exp") v <- exp(input$vv)
v
})
output$msg = renderPrint({
if (is.null(rv$func)) return("not running")
fv <- funcval()
sprintf("%s(%.3f)=%.3f",rv$func,input$vv,fv)
})
}
shinyApp(ui=u,server=s)
Yielding this:
Note that the slider input value formats its current value rather badly when the min and max values are not even. Not sure what one can do about this.

Call eventReactive for an arbitrary number of action buttons

What I'm trying to do is create an arbitrary number of action button, each of which has their own event based on their own individual values.
Let's say we want to create a number of buttons. What we do is draw a random number between 1 and 100 and call it n. Then we create n buttons, each with a value between 1 and n (covering every number once). Then, when we press one of those buttons, we render a text message being the number that we pressed.
To set up the buttons, we have:
ui.R
shinyUI(fluidPage(
actionButton('roll','roll'),
uiOutput('buttons')
))
Server.R
shinyServer(function(input, output) {
n <- eventReactive(input$roll, {
num <- sample(1:100,1)
sample(1:num, num, replace=FALSE)
})
output$buttons <- renderUI({
lapply(1:length(n()), function(i) {
actionButton(as.character(n()[i]), as.character(n()[i]) )
})
})
})
This generates the buttons. However, I'm struggling to find a way to create all the necessary eventReactive()s. I tried calling eventReactive() inside a loop, and in a lapply call. However, in order to make that loop or lapply, you need the value of length(n()), which can only be called inside another reactive or observe command.
Given the buttons generated from the above script, how do we make a reactive expression for each button, and then output the text corresponding to the number pressed?
You can search through the input looking for buttons that have been triggered. Once a button is clicked, its value is greater than 0, so all the picked values will print this way (not sure if that is desired?)
library(shiny)
shinyApp(
shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
actionButton('roll','roll'),
uiOutput('buttons')
),
mainPanel(
textOutput('stuff')
)
)
)),
shinyServer(function(input, output) {
n <- eventReactive(input$roll, {
num <- sample(1:100,1)
sample(1:num, num, replace=FALSE)
})
output$buttons <- renderUI({
lapply(1:length(n()), function(i) {
actionButton(as.character(n()[i]), as.character(n()[i]) )
})
})
output$stuff <- renderText({
val <- which(lapply(paste(n()), function(i) input[[i]]) == TRUE)
if (length(val))
sprintf("Picked %s!", paste(n())[val])
})
})
)

Resources