How to render dynamic UIs based on user selection with shiny - r

Given the set of shiny UIs and their differings arguments (to be read from a rdf, here given as explicit lists) how can the user select a desired type of input (for a data-model with many different inputs, all presetted with defaults) to be changed?
library(shiny)
library(shinyWidgets)
library(DT)
library(purrr)
library(dplyr)
library(data.table)
#-----------------someWidgetsAndArguments-------------------.
inputWidget <- list("selectInput", "sliderInput", "textInput", "numericInput")
inpWidgArgs <- list(list(inputId = "inpUI01", label = "seInp01", choices = "seq(1,20,1)", selected = 10),
list(inputId = "inpUI02", label = "slInp02", min= 0, max = 100, value = "c(25,75)" ),
list(inputId = "inpUI03", label = "txInp03", value = "enter some text"),
list(inputId = "inpUI04", label = "nrInp04", value = 1000000) )
#----------------presetPickerInput---------------------
if (interactive()) {
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
pickerInput(inputId = "pkInp01",
label = "Select CF-Model Inputs for change",
choices = inputWidget,
selected = inputWidget[1:2],
multiple = TRUE,
options = list(`actions-box` = TRUE,
`selected-text-format`= "count",
`count-selected-text` = "{0} inputs of {1} selected") ),
uiOutput("inpUI"),
),
mainPanel(
dataTableOutput("table01")
)
)
)
#-----------------generateArguments4invoke_map---------------------------.
server <- function(input, output, session) {
#B: obs <- reactiveValues(
#A: pckdWdgt <- inputWidget[match(input$pkInp01, inputWidget)],
#A: wdgtArgs <- inpWidgArgs[match(input$pkInp01, inputWidget)]
#B: )
#B: observe({
#B: obs$pW01 = inputWidget[match(input$pkInp01, inputWidget)]
#B: obs$wA02 = inpWidgArgs[match(input$pkInp01, inputWidget)]
#B: })
#------------------renderAsManyInputUisAsPicked------------
output$inpUI <- renderUI({
#A: invoke_map(match.fun(pckdWdgt), wdgtArgs)
#B: invoke_map(match.fun(obs$pW01), obs$WA02)
invoke_map(list(selectInput, sliderInput), list(
list(inputId = "inpUI01", label = "selectInput01", choices = seq(1,20,1), selected = 10),
list(inputId = "inpUI02", label = "sliderInput02", min= 0, max = 100, value = c(25,75) )
)
)
})
}
}
#-----------------------------------------------------
shinyApp(ui, server)
With map() or invoke_map() it should be possible to pass the type of function/UIinput and its arguments (compare: https://hadley.shinyapps.io/ms-render-palette-full).
Two approaches (A: and B:) below fail (possible reason: environment/namespace?) Any suggestion highly appreciated.
Many thanks in advance

I cleaned some of your code and created the solution. To start a few minor things: The choices argument in seInp01 shouldn't be between quotations. The same goes for the value argument in slInp01. Lastly there is a trailing comma behind your uiOutput argument in the UI. For the functionality of the code I just put some codes that you already came up with in the right place, you had the right idea.
The code:
library(shiny)
library(shinyWidgets)
library(DT)
library(purrr)
library(dplyr)
library(data.table)
#-----------------someWidgetsAndArguments-------------------.
inputWidget <- list("selectInput", "sliderInput", "textInput", "numericInput")
inpWidgArgs <- list(list(inputId = "inpUI01", label = "seInp01", choices = seq(1,20,1), selected = 10),
list(inputId = "inpUI02", label = "slInp02", min= 0, max = 100, value = c(25,75) ),
list(inputId = "inpUI03", label = "txInp03", value = "enter some text"),
list(inputId = "inpUI04", label = "nrInp04", value = 1000000) )
#----------------presetPickerInput---------------------
if (interactive()) {
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
pickerInput(inputId = "pkInp01",
label = "Select CF-Model Inputs for change",
choices = inputWidget,
selected = inputWidget[1:2],
multiple = TRUE,
options = list(`actions-box` = TRUE,
`selected-text-format`= "count",
`count-selected-text` = "{0} inputs of {1} selected") ),
uiOutput("inpUI")
),
mainPanel(
dataTableOutput("table01")
)
)
)
#-----------------generateArguments4invoke_map---------------------------.
server <- function(input, output, session) {
#------------------renderAsManyInputUisAsPicked------------
output$inpUI <- renderUI({
wdgtArgs <- inpWidgArgs[match(input$pkInp01, inputWidget)]
invoke_map(input$pkInp01, wdgtArgs)
})
}
}
#-----------------------------------------------------
shinyApp(ui, server)

Related

Calculating sum in Shiny

I'm trying to create a shiny app as a practice planner where users can select which drills they are going to do and how long they will do each drill and the app then shows them the total meters covered for the whole practice. Now I'm trying to calculate the total values of meters covered during a session based on the drills selected and the number of minutes selected for each drill. However my total is always equal to 0 even though it works for calculating each drill separately. Could someone help me figure out what I'm doing wrong please. Below is my code with sample data.
library(shiny)
library(dplyr)
# MyData <- read.csv("/Users/sonamoravcikova/Desktop/ShinyTest/ForShiny1.csv")
MyData <- structure(list(Drill = c("GP Warm Up", "5v2 Rondo", "11v11", "10v6 Drop
Behind Ball"), PlayerLoadPerMinute = c(7.72949670665213, 6.49382926701571,
9.67483408668731, 5.86770863636364), MetersPerMinute = c(69.9524820610687,
45.823744973822, 95.9405092879257, 58.185375), class = "data.frame", row.names
= c(NA, -4L)))
# Define UI ----
ui <- fluidPage(
titlePanel("Practice Planner"),
sidebarLayout(
sidebarPanel(
#Select number of drills
numericInput("num", h3("Number of Drills"), value = 1),
textOutput("MpM_Total")
),
mainPanel(
#Show boxes for the number of drill selected and select drill type
selectInput("DrillName1",
label = "Choose a Drill:",
choices = unique(MyData$Drill),
selected = NULL,
multiple = FALSE),
sliderInput("slider1",
label = h3("Slider"),
min = 0,
max = 60,
value = 0),
textOutput("MpM1"),
br(),
conditionalPanel(
condition = "input.num > '1'",
selectInput("DrillName2",
label = "Choose a Drill:",
choices = unique(MyData$Drill),
selected = NULL,
multiple = FALSE),
sliderInput("slider2",
label = h3("Slider"),
min = 0,
max = 60,
value = 0),
textOutput("MpM2")),
br(),
conditionalPanel(
condition = "input.num > '2'",
selectInput("DrillName3",
label = "Choose a Drill:",
choices = unique(MyData$Drill),
selected = NULL,
multiple = FALSE),
sliderInput("slider3",
label = h3("Slider"),
min = 0,
max = 60,
value = 0),
textOutput("MpM3"))
)
)
)
# Define server logic ----
server <- function(input, output, session) {
#Calculate number of meters covered
lapply(1:10, function(x) {
MetersPerMin <- reactive({
chosendrill <- input[[paste0("DrillName",x)]]
MpM <- MyData %>%
distinct(MetersPerMinute, .keep_all = T)
MpM_text <- (MpM$MetersPerMinute[MpM$Drill == chosendrill]) * (input[[paste0("slider",x)]])
})
output[[paste0("MpM", x)]] <- renderText({
paste0("Meters covered: ", MetersPerMin())
})
MpM_Sum <- reactive({
sum(MetersPerMin())
})
output$MpM_Total <- renderText({
paste("Total Meters Covered", MpM_Sum())
})
})
}
# Create Shiny app ----
shinyApp(ui = ui, server = server)
library(shiny)
library(dplyr)
MyData <- data.frame(Drill = c('GP Warm Up', '5v2 Rondo', '11v11', '10v6 Drop Behind Ball'),
PlayerLoadPerMinute = c(7.72949670665213, 6.49382926701571, 9.67483408668731, 5.86770863636364),
MetersPerMinute = c(69.9524820610687, 45.823744973822, 95.9405092879257, 58.185375))
MpM <- MyData %>%
distinct(MetersPerMinute, .keep_all = T)
# Define UI ----
ui <- fluidPage(
titlePanel('Practice Planner'),
sidebarLayout(
sidebarPanel(
#Select number of drills
numericInput('num', h3('Number of Drills'), value = 1),
textOutput('MpM_Total')
),
mainPanel(
#Show boxes for the number of drill selected and select drill type
selectInput('DrillName1',
label = 'Choose a Drill:',
choices = unique(MyData$Drill),
selected = NULL,
multiple = FALSE),
sliderInput('slider1',
label = h3('Slider'),
min = 0,
max = 60,
value = 0),
textOutput('MpM1'),
br(),
conditionalPanel(
condition = 'input.num > "1"',
selectInput('DrillName2',
label = 'Choose a Drill:',
choices = unique(MyData$Drill),
selected = NULL,
multiple = FALSE),
sliderInput('slider2',
label = h3('Slider'),
min = 0,
max = 60,
value = 0),
textOutput('MpM2')
),
br(),
conditionalPanel(
condition = 'input.num > "2"',
selectInput('DrillName3',
label = 'Choose a Drill:',
choices = unique(MyData$Drill),
selected = NULL,
multiple = FALSE),
sliderInput('slider3',
label = h3('Slider'),
min = 0,
max = 60,
value = 0),
textOutput('MpM3')
)
)
)
)
# Define server logic ----
server <- function(input, output, session) {
MetersPerMin <- reactive({
idx <- input$num
if (idx < 1) {
idx <- 1
} else if (idx > 3) {
idx <- 3
}
mpms <- sapply(1:idx, function(x) {
chosendrill <- input[[ paste0('DrillName', x) ]]
mpm <- (MpM$MetersPerMinute[ MpM$Drill == chosendrill ]) * (input[[ paste0('slider', x) ]])
output[[ paste0('MpM', x) ]] <- renderText(paste0('Meters covered: ', mpm))
mpm
})
mpms
})
output$MpM_Total <- renderText({
paste('Total Meters Covered', sum(MetersPerMin()))
})
}
# Create Shiny app ----
shinyApp(ui = ui, server = server)

How to control an argument of a function depending on a radiobutton choice in R shiny

This is a follow-up to this Dynamic change in vtree within shiny: How to deselect
With this code below, I try to switch the arguments prunesmaller and prunebigger of the vtree package. I am quite sure to do this with an if else but I am not able to fix it:
In general I want to know how to tweak any argument of any function depending on a radiobutton in r shiny:
Here is my code so far:
library(shiny)
library(vtree)
# Define UI ----
ui <- pageWithSidebar(
# App title ----
headerPanel("Cyl vtree"),
# Sidebar panel for inputs ----
sidebarPanel(
radioButtons("smaller_bigger", h3("Prune smaller or bigger?"), choices = c("smaller", "bigger"), inline = TRUE),
sliderInput(inputId = "prune", label = "Number to prune?", step = 10, min = 0, max = 100, value = 0),
selectizeInput("level", label = "Level", choices = NULL, multiple=TRUE),
# This line is the only change from the original code
selectizeInput("values", label= "Values", choices = NULL, multiple=TRUE),
),
# Main panel for displaying outputs ----
mainPanel(
vtreeOutput("VTREE")
)
)
# Define server logic to plot ----
server <- function(input, output,session) {
df <- reactiveVal(mtcars)
vector <- c("cyl","vs", "am","gear")
observe({
updateSelectizeInput(session, "level", choices = colnames(df()[vector]), selected = NULL)
updateSelectizeInput(session, "values", choices = unique(df()$cyl))
})
output[["VTREE"]] <- renderVtree({
vtree(df(), c(input$level),
sameline = TRUE,
follow=list(cyl=input$values),
if(input$smaller_bigger=="smaller"){
prunesmaller = input$prune
} else
(input$smaller_bigger == "bigger"){
prunebigger = input$prune
}
)
})
}
shinyApp(ui, server)
In essence I try to handle this part of the code:
if(input$smaller_bigger=="smaller"){
prunesmaller = input$prune
} else
(input$smaller_bigger == "bigger"){
prunebigger = input$prune
}
)
It should do:
If radiobutton smaller is choosen then the argument should be prunesmaller == input$prune (where input$prune comes from the sliderinput)
If I replace the if else part by prunesmaller = input$prune the code works but only with prunesmaller:
The way you use the if will not work to set the functions argument. Instead use a single if for each argument, e.g. prunesmaller = if (input$smaller_bigger == "smaller") input$prune.
Note: Maybe I missed something, but I got an error when trying to set prunebigger and according to the docs there is no prunebigger argument.
library(shiny)
library(vtree)
# Define UI ----
ui <- pageWithSidebar(
# App title ----
headerPanel("Cyl vtree"),
# Sidebar panel for inputs ----
sidebarPanel(
radioButtons("smaller_bigger", h3("Prune smaller or bigger?"), choices = c("smaller", "bigger"), inline = TRUE),
sliderInput(inputId = "prune", label = "Number to prune?", step = 10, min = 0, max = 100, value = 0),
selectizeInput("level", label = "Level", choices = NULL, multiple = TRUE),
# This line is the only change from the original code
selectizeInput("values", label = "Values", choices = NULL, multiple = TRUE),
),
# Main panel for displaying outputs ----
mainPanel(
vtreeOutput("VTREE")
)
)
# Define server logic to plot ----
server <- function(input, output, session) {
df <- reactiveVal(mtcars)
vector <- c("cyl", "vs", "am", "gear")
observe({
updateSelectizeInput(session, "level", choices = colnames(df()[vector]), selected = NULL)
updateSelectizeInput(session, "values", choices = unique(df()$cyl))
})
output[["VTREE"]] <- renderVtree({
vtree(df(), c(input$level),
sameline = TRUE,
follow = list(cyl = input$values),
prunesmaller = if (input$smaller_bigger == "smaller") input$prune
#prunebigger = if (input$smaller_bigger == "bigger") input$prune
)
})
}
shinyApp(ui, server)

Getting a sum of values in R Shiny

I have this R Shiny that gives me values of Meters covered based on the drill selected and the time selected by the user. Here is my code.
library(shiny)
library(dplyr)
# MyData <- read.csv("/Users/sonamoravcikova/Desktop/ShinyTest/ForShiny1.csv")
MyData <- structure(list(Drill = c("GP Warm Up", "5v2 Rondo", "11v11", "10v6 Drop
Behind Ball"), PlayerLoadPerMinute = c(7.72949670665213, 6.49382926701571,
9.67483408668731, 5.86770863636364), MetersPerMinute = c(69.9524820610687,
45.823744973822, 95.9405092879257, 58.185375), class = "data.frame", row.names
= c(NA, -4L)))
# Define UI ----
ui <- fluidPage(
titlePanel("Practice Planner"),
sidebarLayout(
sidebarPanel(
#Select number of drills
numericInput("num", h3("Number of Drills"), value = 1),
),
mainPanel(
#Show boxes for the number of drill selected and select drill type
selectInput("DrillName1",
label = "Choose a Drill:",
choices = unique(MyData$Drill),
selected = NULL,
multiple = FALSE),
sliderInput("slider1",
label = h3("Slider"),
min = 0,
max = 60,
value = 0),
textOutput("MpM1"),
br(),
conditionalPanel(
condition = "input.num > '1'",
selectInput("DrillName2",
label = "Choose a Drill:",
choices = unique(MyData$Drill),
selected = NULL,
multiple = FALSE),
sliderInput("slider2",
label = h3("Slider"),
min = 0,
max = 60,
value = 0),
textOutput("MpM2")),
br(),
conditionalPanel(
condition = "input.num > '2'",
selectInput("DrillName3",
label = "Choose a Drill:",
choices = unique(MyData$Drill),
selected = NULL,
multiple = FALSE),
sliderInput("slider3",
label = h3("Slider"),
min = 0,
max = 60,
value = 0),
textOutput("MpM3"))
)
)
)
# Define server logic ----
server <- function(input, output, session) {
#Calculate number of meters covered
lapply(1:10, function(x) {
output[[paste0("MpM", x)]] <- renderText({
chosendrill <- input[[paste0("DrillName", x)]]
MpM <- MyData %>%
distinct(MetersPerMinute, .keep_all = T)
MpM_text <- (MpM$MetersPerMinute[MpM$Drill == chosendrill]) * (input[[paste0("slider", x)]])
paste0("Meters covered: ", paste0(MpM_text, collapse = " "))
})
})
}
# Create Shiny app ----
shinyApp(ui = ui, server = server)
Now I'm trying to just add all of the values that I get for the individual drills together so that I will get Meters covered for the whole session but I have no idea how to do that. So if someone could help me out where to start I would appreciate it. Thanks

Create graph based on selection of input and output

New to shiny. I am trying to create a plot based on chosen x and y values. Basically, whatever the user selects for the select1 and select2 selectInput function will graph it accordingly. My original data has many columns, not just two. When I try to graph very specific things, my code works great, but when I try to graph what the user "selects" it does not work.
library(shiny)
library(readr)
library(ggplot2)
library(dplyr)
data0 <- rnorm(n = 10, mean = 100, sd = 5)
data1 <- rnorm(n = 10, mean = 50, sd = 10)
data2 <- data.frame(data0, data1)
attach(data2)
ui <- fluidPage(
selectInput(inputId = "select1",
label = "select the x-axis",
choices = c(" ", "data0", "data1")
),
selectInput(inputId = "select2",
label = "select the y-axis",
choices = c(" ", "data0", "data1")
),
submitButton(text = "Apply Changes", icon = NULL, width = NULL),
plotOutput(outputId = "myplot")
)
server <- function(input, output) {
output$myplot <- renderPlot({
data2 %>%
ggplot(aes(input$select1 ~ input$select2))+
geom_point(alpha = 0.8)
})
}
shinyApp(ui = ui, server = server)
I had to add ggplot(aes(x = get(paste(input$select1)), y = get(paste(input$select2)) to make the input selects work.
library(shiny)
library(readr)
library(ggplot2)
library(dplyr)
data0 <- read_csv("DeltaX_Soil_Properties_Fall2020_Spring2021_Fall2021.csv")
data1 <- data0[!(data0$time_marker_sampled == "-9999"),]
attach(data1)
ui <- fluidPage(
selectInput(inputId = "select1",
label = "select the x-axis",
choices = c(" ", "elevation_navd88", "sediment_accretion", "days_between_sampling_and_deployment", "normalized_accretion", "soil_bulk_density", "soil_organic_matter_content", "soil_organic_carbon", "soil_organic_carbon_density")
),
selectInput(inputId = "select2",
label = "select the y-axis",
choices = c(" ", "elevation_navd88", "sediment_accretion", "days_between_sampling_and_deployment", "normalized_accretion", "soil_bulk_density", "soil_organic_matter_content", "soil_organic_carbon", "soil_organic_carbon_density")
),
submitButton(text = "Apply Changes", icon = NULL, width = NULL),
plotOutput(outputId = "myplot")
)
server <- function(input, output) {
output$myplot <- renderPlot({
data1 %>%
ggplot(aes(x = get(paste(input$select1)), y = get(paste(input$select2)), col = hydrogeomorphic_zone))+
geom_point(alpha = 0.8)
})
}
shinyApp(ui = ui, server = server)
If you want to use a variable as x or y, you can alternatively use aes_() instead of aes().
This would then result in:
ggplot(aes_(x = input$select1, y = input$select2))
Beware, that you need to add a tilde if you want to use a normal column name with aes_(), e.g.:
ggplot(aes_(x = ~elevation_navd88, y = input$select2))

how to control the values entered in selectizeInput in Shiny, making sure they belong to a predifined list?

I would like that when user exits the selectizeInput field (clicks outside of selectizeInput), a new option is created and selected (option createOnBlur = TRUE), but I can't figure out how to control the created values to ensure they belong to the "choices" list.
In fact, I would like createOnBlur=TRUE working with create=FALSE, but this obviously doesn't work..
I have looked at selectize.js documentation and I think createFilter and/or onBlur() options could be useful but I didn't succeed in implementing it for my purpose.
Here is a reprex with an age input, I would like that when user tape e.g. "40" and then clik outside of input without pressing "Enter" (ie onBlur), the value 40 is recorded in the input, but if the user tape e.g "444", this impossible age value is not created in the list of choices :
library(shiny)
input_age <- function(mina = 0, maxa =100){
selectizeInput(inputId = "age",
label = "Age",
choices = c("choose one" = "", mina:maxa),
options = list(create = TRUE,
createOnBlur = TRUE)
)
}
ui <- shinyUI(fluidPage(
titlePanel("selectize createonblur"),
mainPanel(
input_age(mina = 20, maxa = 70)
)
))
# SERVER
server <- shinyServer(function(input, output) {
})
shinyApp(ui, server)
You can use updateSelectizeInput to check the selection made against the choices after each interaction with your input.
Please see the following:
library(shiny)
input_age <- function(mina = 0, maxa = 100){
selectizeInput(inputId = "age",
label = "Age",
choices = c("choose one" = "", mina:maxa),
options = list(create = TRUE,
createOnBlur = TRUE)
)
}
minAge <- 20
maxAge <- 70
ui <- shinyUI(fluidPage(
titlePanel("selectize createonblur"),
mainPanel(
input_age(mina = minAge, maxa = maxAge)
)
))
# SERVER
server <- shinyServer(function(input, output, session) {
observeEvent(req(input$age), {
if(length(setdiff(input$age, as.character(seq(minAge, maxAge)))) > 0){
updateSelectizeInput(session,
inputId = "age",
choices = seq(minAge, maxAge),
selected = "")
}
})
})
shinyApp(ui, server)
Update - Here is a JS approach:
library(shiny)
input_age <- function(mina = 0, maxa = 100){
selectizeInput(inputId = "age",
label = "Age",
choices = c("choose one" = "", mina:maxa),
options = list(create = TRUE,
createOnBlur = TRUE))
}
ui <- shinyUI(fluidPage(
tags$head(tags$script(HTML("
$(document).on('shiny:inputchanged', function(event) {
if (event.name === 'age') {
if (isNaN(parseInt(event.value)) || event.value > 70 || event.value < 20) {
var $select = $('#age').selectize();
var selectize = $select[0].selectize;
selectize.setValue(null, true);
}
}
});
"))),
titlePanel("selectize createonblur"),
mainPanel(
input_age(mina = 20, maxa = 70)
)
))
# SERVER
server <- shinyServer(function(input, output, session) {
})
shinyApp(ui, server)
You can supply a regular expression to the createFilter option. If the user types something which doesn't match this regular expression, then "Add ..." will not appear and it will not be possible to add this item.
library(shiny)
ui <- fluidPage(
titlePanel("selectize createonblur"),
mainPanel(
selectizeInput(
inputId = "age",
label = "Age",
choices = c("choose one" = "", 20:70),
options = list(
create = TRUE,
createOnBlur = TRUE,
createFilter = I("/^([2-6][0-9]|70)$/")
)
)
)
)
server <- function(input, output, session) {}
shinyApp(ui, server)

Resources