A shiny app that appends numbers to a 1D vector - r

I would like this app to add whatever number is selected (above zero) in the numeric input to a 1d vector every time that the button is pressed. It should then present that vector as a list of numbers in a box.
library(shiny)
options(shiny.autoreload = TRUE)
ui <- dashboardPage(
dashboardHeader(title = "minrep"),
dashboardSidebar(
numericInput("number",
label = "Enter a number",
value = 0,
min = 1,
max = 100000),
actionButton(
"add.number",
label = "add a number"
),
box(
title = "List of numbers",
span(
textOutput("numbers"),
style = "color:black"
)
)
),
dashboardBody()
)
server <- function(input, output, session) {
list_numbers <- c()
new_number <-
eventReactive(input$add.number, {
input$number
})
observeEvent(input$add.number,{
list_numbers <- append(list_numbers, new_number())
})
output$numbers <- renderText(
list_numbers
)
}
shinyApp(ui, server)

Sure, the trick will be to store our vector as a reactiveValue, so we can access it and change it from wherever we want.
library(shiny)
ui <- fluidPage(
numericInput("number", label = "Enter a number", value = 1, min = 1, max = 100000),
actionButton("add.number", label = "add a number"),
textOutput("numbers")
)
server <- function(input, output, session) {
#Reactive value to store our vector
reactives <- reactiveValues(
list_numbers = c()
)
#Button is pressed
observeEvent(input$add.number, {
reactives$list_numbers <- append(reactives$list_numbers, input$number)
})
#Textbox Output
output$numbers <- renderText(
reactives$list_numbers
)
}
shinyApp(ui, server)

Related

R Shiny - Hiding an Output When a Value Changes

I Would like to make a form type Shiny app where the output is shown when a user clicks and action button, but the output is subsequently hidden if an input values changes.
Here is an example:
library(shiny)
library(shinyjs)
ui <- fluidPage(
radioButtons("myRadioButton", label = h4("Radio Input"),
choices = list("A" = 0,
"B" = 1,
"C" = 2),
selected = character(0)),
numericInput("myNumericInput", label = h4("Numeric Input"),
value = NA, min = 0, max = 50, step = 1),
actionButton("submit", "Submit"),
textOutput("myOutput")
)
server <- function(input, output, session){
score <- reactive({
scoreOut <- paste(input$myRadioButton, input$myNumericInput)
})
observeEvent(input$myRadioButton, {
hide("myOutput")
})
observeEvent(input$myNumericInput, {
hide("myOutput")
})
observeEvent(input$submit, {
show("myOutput")
output$myOutput <- renderText({
paste("This is your value:", score())
})
})
}
shinyApp(ui, server)
So in the above example the output displays after "Submit" is clicked. What I would like is if you go back and change say the radio or numeric input, the output disappears until "Submit" is clicked again.
You missed the useShinyjs() in the UI to load the JavaScript required to execute the hide function:
library(shiny)
library(shinyjs)
ui <- fluidPage(
# load required Java Script
useShinyjs(),
radioButtons("myRadioButton",
label = h4("Radio Input"),
choices = list(
"A" = 0,
"B" = 1,
"C" = 2
),
selected = character(0)
),
numericInput("myNumericInput",
label = h4("Numeric Input"),
value = NA, min = 0, max = 50, step = 1
),
actionButton("submit", "Submit"),
textOutput("myOutput")
)
server <- function(input, output, session) {
score <- reactive({
scoreOut <- paste(input$myRadioButton, input$myNumericInput)
})
observeEvent(input$myRadioButton, {
hide("myOutput")
})
observeEvent(input$myNumericInput, {
hide("myOutput")
})
observeEvent(input$submit, {
show("myOutput")
output$myOutput <- renderText({
paste("This is your value:", score())
})
})
}
shinyApp(ui, server)

Shiny Dynamic UI Resetting to Original Values

I have created a dynamic UI with the number of rows of a 'table' defined by a slider. I would like to use the numericInputs from the UI to perform further calculations. In the example below I have tried to calculate a rate from the two numeric inputs, which seems to work when new values are entered but immediately defaults back to the original starting values.
I tried using a button and changing the observe to an observeEvent to calculate the rates which worked to generate the result, but did not stop the numericInputs defaulting back to the starting values.
I have also tried to create the textboxes as a reactive and then call it to renderUI which gives the same 'broken' functionality.
output$groupings <- renderUI({ textboxes() })
textboxes <- reactive ({
I think I need to create vector or datatable to store the inputs so that I can call them later, however I've been unsuccessful so far. My working example is below:
library(shiny)
mod1UI <- function(id) {
ns <- NS(id)
tagList(
sliderInput(inputId = ns("groups"), label = "Number of Rows", min = 1, max = 6, value = 4, step = 1, width = NULL),
hr(),
fluidRow(
column(2,
strong("Speed")),
column(2,
strong("Amount")),
column(2,
strong("Run Rates"))
),
hr(),
uiOutput(ns("textboxes")),
)
}
mod1 <- function(input, output, session, data) {
ns <- session$ns
m <- reactiveValues(x=NULL)
output$textboxes <- renderUI ({
req(input$groups)
lapply(1:input$groups, function(i) {
fluidRow(
column(2,
numericInput(inputId = paste0(session$ns("speed"),i), value = 700, label = NULL, width = 80)
),
column(2,
numericInput(inputId = paste0(session$ns("amount"),i), value = 14, label = NULL, width = 80)
),
column(2,
(m$x[[i]])
)
)
})
})
observe({
lapply(1:input$groups, function(i){
m$x[[i]] <- input[[paste0("speed", i)]] * input[[paste0("amount", i)]] * 60
})
})
}
ui <- fluidPage(
fluidRow(
column(12,
mod1UI("input1"))
)
)
server <- function(input, output, session) {
y <- callModule(mod1, "input1")
}
shinyApp(ui, server)
Your problem is that you render all elements to one output, output$textboxes. Changing the input value of one of your numeric inputs leads to the calculation of a new rate, so the reactive Value m gets updated and the output$textboxes is rerendered.
Below I present you a solution where the different columns are rendered separately; you would have to play with HTML/CSS to display the values nicely. However, if you change the numbers of rows with the slider, all inputs are reset. Therefore I also added a solution where every row is a module that can be added.
library(shiny)
mod1UI <- function(id) {
ns <- NS(id)
tagList(
sliderInput(inputId = ns("groups"), label = "Number of Rows", min = 1, max = 6, value = 4, step = 1, width = NULL),
hr(),
fluidRow(
column(2,
strong("Speed")),
column(2,
strong("Amount")),
column(2,
strong("Run Rates"))
),
hr(),
fluidRow(
column(2,
uiOutput(ns("UI_speed"))),
column(2,
uiOutput(ns("UI_amount"))),
column(2,
uiOutput(ns("rates")))
)
)
}
mod1 <- function(input, output, session, data) {
ns <- session$ns
m <- reactiveValues(x=NULL)
output$UI_speed <- renderUI({
req(input$groups)
lapply(1:input$groups, function(i) {
numericInput(inputId = paste0(session$ns("speed"),i), value = 700, label = NULL, width = 80)
})
})
output$UI_amount <- renderUI({
req(input$groups)
lapply(1:input$groups, function(i) {
numericInput(inputId = paste0(session$ns("amount"),i), value = 14, label = NULL, width = 80)
})
})
output$rates <- renderUI({
req(input$groups)
text <- lapply(1:input$groups, function(i) {
m$x[[i]]
})
HTML(paste0(text, collapse = "<br>"))
})
observe({
lapply(1:input$groups, function(i){
m$x[[i]] <- input[[paste0("speed", i)]] * input[[paste0("amount", i)]] * 60
})
})
}
ui <- fluidPage(
fluidRow(
column(12,
mod1UI("input1"))
)
)
server <- function(input, output, session) {
y <- callModule(mod1, "input1")
}
shinyApp(ui, server)
Every row is a module
You get more flexibility if you have the slider in the main app and then add/remove a module. The module UI now consists of a set of inputs for Speed and Amount and an Output for the Rate. You can use insertUI and removeUI to dynamically control the amount of modules and with this the amount of displayed UI elements.
library(shiny)
mod1UI <- function(id) {
ns <- NS(id)
fluidRow(
id = id,
column(2,
uiOutput(ns("UI_speed"))),
column(2,
uiOutput(ns("UI_amount"))),
column(2,
textOutput(ns("rates")))
)
}
mod1 <- function(input, output, session, data) {
ns <- session$ns
output$UI_speed <- renderUI({
numericInput(inputId = ns("speed"), value = 700, label = NULL, width = 80)
})
output$UI_amount <- renderUI({
numericInput(inputId = ns("amount"), value = 14, label = NULL, width = 80)
})
output$rates <- renderText({
get_rate()
})
get_rate <- reactive({
input$speed * input$amount * 60
})
}
ui <- fluidPage(
fluidRow(
column(12,
sliderInput(inputId = "groups", label = "Number of Rows", min = 1, max = 6, value = 4, step = 1, width = NULL),
hr(),
fluidRow(
column(2,
strong("Speed")),
column(2,
strong("Amount")),
column(2,
strong("Run Rates"))
),
hr(),
tags$div(id = "insert_ui_here")
)
)
)
number_modules <- 4
current_id <- 1
server <- function(input, output, session) {
# generate the modules shown on startup
for (i in seq_len(number_modules)) {
# add the UI
insertUI(selector = '#insert_ui_here',
ui = mod1UI(paste0("module_", current_id)))
# add the logic
callModule(mod1, paste0("module_", current_id))
# update the id
current_id <<- current_id + 1
}
observeEvent(input$groups, {
# add modules
if (input$groups > number_modules) {
for (i in seq_len(input$groups - number_modules)) {
# add the UI
insertUI(selector = '#insert_ui_here',
ui = mod1UI(paste0("module_", current_id)))
# add the logic
callModule(mod1, paste0("module_", current_id))
# update the id
current_id <<- current_id + 1
}
} else {
# remove modules
for (i in seq_len(number_modules - input$groups)) {
# remove the UI
removeUI(selector = paste0("#module_", current_id - 1))
current_id <<- current_id - 1
}
}
# update the number of modules
number_modules <<- input$groups
}, ignoreInit = TRUE)
}
shinyApp(ui, server)

Hide plot when action button or slider changes in R Shiny

I have a small Shiny app that generates some data whenever the New data button is pressed. The Show plot button shows a hidden plot. I would like the plot to be hidden again automatically whenever the New data button is pressed to make a new data set. A bonus would be for the plot to be hidden also as soon as the slider is changed. I am not looking for a toggle action.
I tried adapting this example that uses conditional panel but I could not successfully figure out how to correctly change the values$show between TRUE and FALSE.
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput(inputId = "number",
label = "Pick a number",
min = 6,
max = 12,
value = 8),
actionButton("new_data",
"New data"),
actionButton("show_plot",
"Show plot")
),
mainPanel(
tableOutput("char_table"),
plotOutput(outputId = "car_plot")
)
)
)
server <- function(input, output) {
t <- eventReactive(input$new_data, {
r <- input$number
c <- r - 1
mat <- matrix(sample(0:1,r*c, replace=TRUE),r,c)
})
output$char_table <- renderTable({
t()
})
p <- eventReactive(input$show_plot, {
plot(cars)
})
output$car_plot <- renderPlot({
p()
})
}
shinyApp(ui = ui, server = server)
You can use a reactive value and a if to control the plot.
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput(inputId = "number",
label = "Pick a number",
min = 6,
max = 12,
value = 8),
actionButton("new_data",
"New data"),
actionButton("show_plot",
"Show plot")
),
mainPanel(
tableOutput("char_table"),
plotOutput(outputId = "car_plot")
)
)
)
server <- function(input, output) {
showPlot <- reactiveVal(FALSE)
t <- eventReactive(input$new_data, {
showPlot(FALSE)
r <- input$number
c <- r - 1
mat <- matrix(sample(0:1,r*c, replace=TRUE),r,c)
})
output$char_table <- renderTable({
t()
})
observeEvent(input$number, {
showPlot(FALSE)
})
observeEvent(input$show_plot, {
showPlot(TRUE)
})
output$car_plot <- renderPlot({
if (showPlot())
plot(cars)
})
}
shinyApp(ui = ui, server = server)
Alternate solution using shinyjs which is handy in these situations.
library(shiny)
library(shinyjs)
ui <- fluidPage( shinyjs::useShinyjs(),
sidebarLayout(
sidebarPanel(
sliderInput(inputId = "number",
label = "Pick a number",
min = 6,
max = 12,
value = 8),
actionButton("new_data",
"New data"),
actionButton("show_plot",
"Show plot")
),
mainPanel(
tableOutput("char_table"),
plotOutput(outputId = "car_plot")
)
)
)
server <- function(input, output) {
t <- eventReactive(input$new_data, {
hide("car_plot")
r <- input$number
c <- r - 1
mat <- matrix(sample(0:1,r*c, replace=TRUE),r,c)
})
output$char_table <- renderTable({
t()
})
observeEvent(input$show_plot, {
show("car_plot")
})
output$car_plot <- renderPlot({
plot(cars)
})
}
shinyApp(ui = ui, server = server)

Using a reactive value in an IF-statement in the UI in R Shiny

I am trying to create a conditional UI in Shiny that depends on the input of a user. I specifically want to do the if in the UI part and NOT in the server part.
Here is an example of what I aim to accomplish.
# app.R
library(shiny)
ui <- shiny::fluidPage(
shiny::headerPanel(title = "Basic App"),
shiny::sidebarPanel(
shiny::sliderInput(inputId = "a",
label = "Select an input to display",
min = 0, max = 100, value = 50
)
),
if(output$out < 50){
shinyjs::hide(shiny::mainPanel(h1(textOutput("text"))))
}else{
shiny::mainPanel(h1(textOutput("text")))
}
)
server <- function(input, output) {
output$text <- shiny::renderText({
print(input$a)
})
var <- shiny::reactive(input$a)
output$out <- renderText({ var() })
}
shiny::shinyApp(ui = ui, server = server)
Is there a way that I can use the reactive value in the UI part of the function?
I think conditionalPanel could be a good solution for what you want to do
library(shiny)
ui <- shiny::fluidPage(
shiny::headerPanel(title = "Basic App"),
shiny::sidebarPanel(
shiny::sliderInput(inputId = "a",
label = "Select an input to display",
min = 0, max = 100, value = 50
)
),
shiny::mainPanel(
conditionalPanel(
condition = "input.a > 50",
h1(textOutput("text")))
)
)
server <- function(input, output) {
output$text <- shiny::renderText({
print(input$a)
})
}
shiny::shinyApp(ui = ui, server = server)
Hope this helps!!

Getting an input list containing inputs present in the current session

I want to retrieve the list of inputs in the current shiny session.
We can retrieve the list of inputs using names(input).
I have a uiOutput and based on different conditions I am rendering different types inputs. The current problem I am facing is that when the condition changes the inputs from previous renderUI is also present in the list. Is there a way to get only the inputs in the current session?
To explain my query better I have the following sample code:
library(shiny)
ui <- fluidPage(
sliderInput(inputId = "slider",label = "", min = 1, max = 3, value = 1),
uiOutput("UI"),
actionButton(inputId = "btn", label = "Show Inputs"),
verbatimTextOutput(outputId = "textOp")
)
server <- function(input, output){
observeEvent(input$slider,{
if(input$slider == 1){
output$UI <- renderUI(
textInput("txt1",label = "Slider in position 1")
)
}else if(input$slider == 2){
output$UI <- renderUI(
textInput("txt2",label = "Slider in position 2")
)
}else{
output$UI <- renderUI(
textInput("txt3",label = "Slider in position 3")
)
}
})
observeEvent(input$btn,{
output$textOp <- renderText(
paste0(names(input), ",")
)
})
}
shinyApp(ui = ui, server = server)
In the above code when I first click on action button labelled "Show Input" I get the following text as the output:
btn, slider, txt1,
Now when I move the slider to 2 my output is as follows:
btn, slider, txt1, txt2,
Here txt1 was generated when the slider was at position 1, and this renderUI was overridden by output$UI <- renderUI(textInput("txt2",label = "Slider in position 2")). I want an input list where txt1 is not there. Is there a way to do that?
I came up with kind of a workaround, assuming you dont have any inputs that should take a value of NULL. You could set the values of the inputs, that you wish to remove, to NULL and filter for non - NULLs when you display the names.
library(shiny)
ui <- fluidPage(
tags$script("
Shiny.addCustomMessageHandler('resetValue', function(variableName) {
Shiny.onInputChange(variableName, null);
});
"
),
sliderInput(inputId = "slider",label = "", min = 1, max = 3, value = 1),
uiOutput("UI"),
actionButton(inputId = "btn", label = "Show Inputs"),
verbatimTextOutput(outputId = "textOp")
)
server <- function(input, output, session){
observeEvent(input$slider,{
for(nr in 1:3){
if(nr != input$slider) session$sendCustomMessage(type = "resetValue", message = paste0("txt", nr))
}
})
output$UI <- renderUI(
textInput(paste0("txt", input$slider), label = paste0("Slider in position ", input$slider))
)
global <- reactiveValues()
observe({
inp = c()
for(name in names(input)){
if(!is.null(input[[name]])){
inp <- c(inp, name)
}
}
isolate(global$inputs <- inp)
})
output$textOp <- renderText({
global$inputs
})
}
shinyApp(ui = ui, server = server)

Resources