I'm trying to dynamically display different selection menus in shiny based on a user response to a previous menu. For example if the user selects a 'campaign_id' of Launch Sequence, they should get a drop down menu with the appropriate options. However, if they select Sale Event they should get a numeric text entry for the discount %. I've created all the appropriate selection menus, but now I need to figure out how to dynamically display them. I tried using ifelse logic based on the response to the previous question, but it always displays the menu associated with all false responses.
I suspect that when the logic is evaluating the previous response that response isn't formatted correctly, or has some html in there so the logic always evaluates to false. I've included a simplified example of what I'm experiencing. Any help is greatly appreciated.
library(shiny)
library(tidyverse)
# data referenced 1
cid <- tibble(
name = c(
'Free Content',
'Launch Sequence',
'Mega Sale',
'Sale Email',
'Sale Reminder',
'VSL Push'
),
abbr = c('FC',
'LS',
'MS',
'SE',
'SR',
'VP')
)
# data referenced 2
csid <- tibble(
name = c(
'Free Content',
'Launch Sequence',
'Launch Sequence',
'Launch Sequence',
'Launch Sequence',
'Launch Sequence',
'Mega Sale',
'Sale Email',
'Sale Reminder',
'VSL Push'
),
subname = c(
'topic text entry',
'Launch Sequence1',
'Launch Sequence2',
'Launch Sequence3',
'Launch Sequence4',
'Launch Sequence5',
'NA',
'discount numeric entry',
'NA',
'url'
),
abbr = c(
'NA1',
'LS1',
'LS2',
'LS3',
'LS4',
'LS5',
'NA2',
'NA3',
'NA4',
'NA5'
)
)
ui <- fluidPage(
titlePanel("Example Selection"),
sidebarLayout(
sidebarPanel(
selectInput(
inputId = 'campaign_id',
label = 'Campaign ID',
choices = c(cid$name)
),
uiOutput(ifelse(
# which selection menu to display based on campaign_id
'cid' == 'Launch Sequence',
'ls',
ifelse(
'cid' == 'Free Content',
'fc',
ifelse(
'cid' == 'Sale Email',
'se',
ifelse('cid' == 'VSL Push',
'vp',
'mssr')
)
)
)),
textInput(
inputId = 'date',
label = 'Launch Date',
placeholder = paste0('use date format "', Sys.Date(), '"')
)
),
############### Main panel for displaying outputs ----
mainPanel(
textOutput(outputId = "campaign_name"))
))
############## server functions --------------------------------------
server <- function(input, output) {
# build campaign sub-id list based on campaign selected ----
cidselected <- reactive({
input$campaign_id
})
output$cid <- renderText({
cidselected()
})
output$ls = renderUI({
selectInput(
inputId = 'campaign_subid',
label = 'Campaign Sub-ID',
choices = c(
'Launch Sequence1',
'Launch Sequence2',
'Launch Sequence3',
'Launch Sequence4',
'Launch Sequence5'
)
)
})
output$fc = renderUI({
textInput(inputId = 'campaign_subid',
label = 'Campaign Sub-ID',
placeholder = '1-3 word description of main topic')
})
output$vp = renderUI({
textInput(inputId = 'campaign_subid',
label = 'Campaign Sub-ID',
placeholder = 'enter VSL url here')
})
output$mssr = renderUI({
selectInput(inputId = 'campaign_subid',
label = 'Campaign Sub-ID',
choices = c('N/A'))
})
output$se = renderUI({
numericInput(
inputId = 'campaign_subid',
label = 'Campaign Sub-ID (enter as whole number 40% = 40)',
value = 0,
min = 0,
max = 100
)
})
output$campaign_name <-
renderText({
# display combined name with abbreviations
cid = cid$abbr[cid$name == input$campaign_id]
csid = input$campaign_subid
date = gsub('-', '', input$date, fixed = T)
paste(cid,
csid,
date,
sep = '-')
})
}
shinyApp(ui = ui, server = server)
It sounds like you're looking for conditional panels. There are more complex methods if you have more input selections/combinations (you can create a uiOutput which is dynamically created in server.R, for one), but this is the simple and straightforward way to do what you want.
Related
I can use conditionals in the UI for input fields without involving the server side. But when I try to do it for things like input_rows_selected or the state of a button; they don't work.
Below I have a couple of input fields set up with conditionals. The 2nd field depends on something in the first being selected. Then the first button depends on something in the second field being selected.
This is where it goes wrong. The third button is supposed to only show up if rows are selected in the datatable. I have gotten it working if ONE row is selected, but not more than one row. And then the html and table below the third button is only supposed to be displayed once the third button is pressed. Currently the html is displayed, and the table doesn't work.
Ideas? I am hoping to keep it as much as possible in the UI side. That way I can easily adapt the button hide/display functionality to various other chunks of code. But I'm not sure if that's possible.
EDIT: UPDATED TO CURRENT CODE
EDIT 2: UPDATED TO FINAL WORKING CODE
if (interactive()) {
library(shiny)
items <- data.frame(
category = c("Room", "IceBreaker", "Activity", "Break"),
group = c(1, 2, 3, 4),
className = c ("red_point", "blue_point", "green_point", "purple_point"),
content = c("Big Room", "Introductions", "Red Rover", "Lunch"),
length = c(480, 60, 120, 90)
)
ui <- fluidPage(shinyUI(
tagList(
useShinyalert(),
useShinyjs(),
title = "Conditional Inputs",
tabsetPanel(id = "mainTabset",
tabPanel(
title = "Wheeeeeee!",
class = "inputs",
column(
12,
selectInput(
inputId = "input1",
label = "A, B, or C?",
choices = c(
Choose = '',
A = 'a',
B = 'b',
C = 'c'
),
selectize = FALSE
),
conditionalPanel(
condition = "input.input1 !== ''",
selectInput(
inputId = "input2",
label = "D, or E?",
choices = c(Choose = '',
D = 'd',
E = 'e'),
selectize = FALSE
)
),
conditionalPanel(
condition = "input.input2 !== ''",
actionButton(
"button1",
"SUBMIT",
style = "background-color:#221B70;
color:#E0EB15;
border-color:#E61029;
border-style:double;
border-width:4px;
border-radius:50%;
font-size:19px;"
),
DT::dataTableOutput("tbl1"),
conditionalPanel(
condition = "typeof input.tbl1_rows_selected !== 'undefined' && input.tbl1_rows_selected.length > 0",
actionButton(
"button2",
"GENERATE TABLE",
style = "background-color:#221B70;
color:#E0EB15;
border-color:#E61029;
border-style:double;
border-width:4px;
border-radius:50%;
font-size:19px;"
)
),
conditionalPanel(condition = "input.button2 > 0",
div(
"Selected items:", DT::dataTableOutput("tbl2")
))
)
)
))
)
))
server <- function(input, output) {
observeEvent(input$button1, {
output$tbl1 <- DT::renderDataTable({
items
}, selection = 'multiple',
class = "display nowrap compact",
extensions = 'Scroller',
options = list(dom = 'Bfrtip'))
})
observeEvent(input$button2, {
table <- items[input$tbl1_rows_selected,c(2,3,4)]
output$tbl2 <- DT::renderDataTable({
table
})
})
}
shinyApp(ui, server)
}
input$tbl1_rows_selected is a single integer if only one row is selected, it is a vector if several rows are selected, and it is NULL if no row is selected. So the appropriate condition is
"input.tbl1_rows_selected !== null"
input$button2 is always an integer. It is initialized to 0 and then it increments each time the button is pressed. The condition to check that the button has been pressed is then
"input.button2 > 0"
I don't see what you mean by "the table below the third button". I don't see any table below this button.
EDIT
That does not work:
"input.tbl1_rows_selected !== null"
That works:
"input.tbl1_rows_selected.length > 0"
It seems that input.tbl1_rows_selected is an empty JavaScript array when input$tbl1_rows_selected is NULL in R. Otherwise it is a non-empty array
I want to do the same thing but with dateInput(). How would I go about outputting the default date set in the dateInput() to say a verbatimTextOutput()?
Code:
library(shiny)
library(shinythemes)
ui <- fluidPage(
theme = shinytheme('slate'),
sidebarPanel(
selectInput(
'country',
'Select a Country',
c('Japan', 'China', 'USA'),
selected = 'Japan'
),
dateInput(
'date',
label = 'Select a Date',
value = as.character(as.Date('05/10/20', '%m/%d/%y')),
min = as.Date('01/22/20', '%m/%d/%y'),
max = as.Date('05/10/20', '%m/%d/%y'),
format = 'mm/dd/yy',
startview = 'month',
weekstart = 1
),
actionButton('resetBtn', 'Reset'),
actionButton('plotBtn', 'Plot', class = 'btn-primary')
),
mainPanel(
verbatimTextOutput('dateText', placeholder = TRUE)
)
)
server <- function(input, output) {
dateInput <- eventReactive(input$plotBtn, {
input$date
}, ignoreNULL = FALSE)
output$dateText <- renderText({
as.character(dateInput())
})
}
shinyApp(ui, server)
The above code still doesn't work and I can't find an answer online that matches this.
Use renderPrint instead of renderText.
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!
I have a question about shiny app. When there is no value select in numeric input and selectizeInput, my shiny app will show an error because of the empty data frame. I would like to hide the error message if user haven't select their input yet. I know if return will help but it seems not working in this app.
server.r:
library(shiny)
# Define server logic required to draw a histogram
shinyServer(function(input, output) {
result<-reactive({
if(is.null(input$wt)||is.null(input$hdcount)||is.null(input$season)||is.null(inp ut$gender) )return(NULL)
mod1<-lm(deathLog ~ InHdCnt+ log(InHdCnt) + season+ SexCode+ AvgArrivWt, data=mydata)
newdata = data.frame(AvgArrivWt=input$wt,InHdCnt=input$hdcount,SexCode=input$gender,season=input$season)
data<-predict(mod1, newdata, interval="predict",level=(input$slider1)*0.01 )
data
})
output$distPlot <- renderPrint({
result()
})
})
ui.r:
library(shiny)
# Define UI for application that draws a histogram
shinyUI(fluidPage(
# Application title
titlePanel("Death Loss Estimator with On Arrival Factors"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
numericInput("wt", label = h4("Average Arrival Weight input"),value="NULL"),
numericInput("hdcount", label = h4("Arrival Head Count input"),value="NULL"),
selectizeInput(
'season', h4('Arrival Season'), choices = c("spring", "summer","fall", "winter"),
options = list(
placeholder = 'Please select a season below',
onInitialize = I('function() { this.setValue(""); }')
)
),
selectizeInput(
'gender', h4('Arrival Sex'), choices = c("HOL", "FEM","MAL", "MIX"),
options = list(
placeholder = 'Please select a season below',
onInitialize = I('function() { this.setValue(""); }')
)
),
sliderInput("slider1", label = h4("Confidence Interval Level"), min = 50,
max = 100, value = 80)
),
# Show a plot of the generated distribution
mainPanel(
textOutput("distPlot")
)
)
))
Thanks!
I would recommend using validate and need. You can put this at the top of your reactive expression:
validate(
need(input$wt, "Please select a weight"),
need(input$hdcount, "Please select a head count")
)
Alternatively you can use req:
req(input$wt)
req(input$hdcount)
Hi I'm using both selectize and dropdownbuttons to build multiple choices select widget for my user. It works fine when single choice is made, however when you make multiple choices, the aggregate data showed is incorrect.
Here is my code:
ui:
library(shiny)
shinyUI(fluidPage(
dropdownButton(
label = "Country", status = "default", width = 400,
checkboxGroupInput(inputId = "var", label = "Country",
choices = c("ALL", "Null", "A2", "AE", "AF"),
selected = c("ALL") )
),
hr(),
dropdownButton(
label = "Platform ", status = "default", width = 400,
checkboxGroupInput(inputId = "plat", label = "Platform ",
choices = c("ALL", "Android","Ios"),
selected = c("Android"))
),
hr(),
selectizeInput(inputId ='media',
label = "Source",
choices = c("ALL", "facebook", "twitter"),
selected = c("ALL"),
multiple = T),
),
mainPanel(
textOutput("text1")
)
))
server:
#server.R
library(shiny)
library(ggplot2)
library(dplyr)
df <- df
shinyServer(function(input, output, session) {
datasetInput <- reactive({
df<- df %>%
filter(
(input$var == 'ALL' | country == input$var)&
(input$plat == 'ALL' | platform == input$plat)&
(input$media == 'ALL' | media_source == input$media)
)
})
output$text1 <- renderText({paste("Revenue: $", df() %>%
summarise(revenue = sum(price)/100)})
})
When I make single choice, the data is correct, ex) when I choose facebook for media, the revenue is 200,000, when i choose twitter, the revenue is 50,000. So I want to see 250,000 when I choose both. however, when the number is quite odd when i'm doing so, I got something like 160,000, which is hard to know how does the system calculate it. Same problems with the other two select widget.
BTW, I'm also getting this warning message:
longer object length is not a multiple of shorter object length
Anyone knows which part I did wrong? Thank you.