My shiny application has multiple tabs. In one of the tabs I have plot output which I want to use to create reports in another tab. I have included a checkbox in the first tab for the user to select the output for reporting. In the second tab I am trying to update a check box group input based on the selection of the first tab. However I am getting only the first option selected.
The reproducible code is as follows: This is based on ifelse condition:
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(
title = "MODULE",titleWidth = 225
),
dashboardSidebar(
width = 225,
sidebarMenu(id = "tabs",
menuItem("TOPLINES", tabName = "tplines", icon = shiny::icon("dashboard")),
menuItem("MY MONTHLY REPORTS", tabName = "myweeklyrep", icon = shiny::icon("compass"))
)),
dashboardBody(
tabItems(
tabItem(
tabName = "tplines",
fluidRow(
box(
checkboxInput(inputId = "inventorytop8metrocheck", "Add to reports", value = FALSE),
width = 6, status = "info", title = "Inventory information",
div(plotlyOutput("inventorytop8metro"), width = "100%", height = "400px", style = "font-size:80%;")
),
box(
checkboxInput(inputId = "top15categoriestplinescheck", "Add to reports", value = FALSE),
width = 6, status = "info", title = "Top 15 categories",
div(plotlyOutput("top15categoriestplines"), style = "font-size:90%")
))),
tabItem(
tabName = "myweeklyrep",
fluidRow(
h4("AVAILABLE ANALYSIS", align = 'center'),br(),
column(width = 12,
list(tags$div(align = 'left',
class = 'multicol',
checkboxGroupInput(inputId = 'analysisSelector',
label = "Select the analysis:",
choices = "",
selected = "",
inline = FALSE)))
))))))
server <- function(session,input,output){
observe({
updateCheckboxGroupInput(session, inputId = "analysisSelector", label = "", choices =
ifelse(!is.null(input$top15categoriestplinescheck) || length(input$top15categoriestplinescheck) != 0, "Inventory top 8 metros",
ifelse(!is.null(input$inventorytop8metrocheck) || length(input$inventorytop8metrocheck) != 0, "Top 15 categories - Topline", "No selection")),
selected = "",inline = FALSE)
})
}
shinyApp(ui,server)
I tried with if, else if also but they aren't working. Any thoughts?
The if, else if conditions:
updateCheckboxGroupInput(session, inputId = "analysisSelector", label = "", choices =
if(!is.null(input$top15categoriestplinescheck) || length(input$top15categoriestplinescheck) != 0){
"Inventory top 8 metros"
} else if (!is.null(input$inventorytop8metrocheck) || length(input$inventorytop8metrocheck) != 0){
"Top 15 categories - Topline"
} else {
return()
},
selected = "",inline = FALSE)
EDIT:
I tried the following option: which renders the checkboxes irrespective of whether they are selected or not.
getlist <- reactive({
if(!is.null(input$top15categoriestplinescheck) & !is.null(input$inventorytop8metrocheck)){
c("Top 15 categories - Topline","Inventory of top 8 metros - Topline")
} else if (!is.null(input$top15categoriestplinescheck)){
"ABC"
} else if (!is.null(input$inventorytop8metrocheck)){
"DEF"
} else {
return()
}
})
observe({
updateCheckboxGroupInput(session, inputId = "analysisSelector", label = "Select the analysis:", choices =
as.list(getlist()),
selected = "",inline = FALSE)
})
This is actually easier to handle with observeEvent as explained in the documentation of this function (see ?observeEvent). From what I understand, it actually wraps observe but in a more intuitive way.
You have to pass it two arguments: an event (in this case, the click on one of your checkboxGroupInputs) and the action to perform when this event occurs.
The server function thus becomes:
server <- function(session,input,output){
updateAnalysisSelector <- function(session) {
choices <- ifelse(input$top15categoriestplinescheck, "Inventory top 8 metros",
ifelse(input$inventorytop8metrocheck, "Top 15 categories - Topline", "No selection"))
updateCheckboxGroupInput(session,
inputId = "analysisSelector",
label = "Select the analysis:",
choices = choices,
selected = "",
inline = FALSE)
}
observeEvent(input$top15categoriestplinescheck, updateAnalysisSelector(session))
observeEvent(input$inventorytop8metrocheck, updateAnalysisSelector(session))
}
I'm sure this could be simplified if your UI did not have two separate checkbox groups but this works for your current implementation.
Related
I need to update/reverse two inputs from drop down inputs upon a button press. At the moment when I hit the swap button (reverse_xz), it reacts however the updatePickerInput doesn't switch my x and z inputs.
I wanted to have the functionality where, once the swap button is clicked, switch the already selected pickerInputs. Then, all the drop down choices (including the selected) need to get reversed. The reason we have to remove the selected choices from vector is to prevent duplicate selections in both x and z inputs.
I am not sure if I have to render the pickerInput ui on the server side?!
This is my code below:
#global.R
library(shiny)
library(shinydashboard)
library(shinydashboardPlus)
library(shinyWidgets)
library(shinyjs)
#variable labels
my_vars <- c("None"= "NONE",
"All" = "all_all",
"Pro" = "Pro_",
"Locomania" = "locomania_Type",
"Racer" = "race")
#ui.R
ui <- shinydashboardPlus::dashboardPage(
header = shinydashboardPlus::dashboardHeader( ),
body = shinydashboard::dashboardBody( box(textOutput("inputs") ) ),
sidebar = shinydashboardPlus::dashboardSidebar(
shinyWidgets::pickerInput(
inputId = "xvar",
label = "X Axis: ",
choices = my_vars,
options = list(
size = 5),
multiple = FALSE,
selected = "all_all"
),
# Button to reverse the choices
shiny::fluidRow(
shiny::column(12, offset = 4,
shinyWidgets::actionBttn(
inputId = "reverse_xz",
label = "",
style = "simple",
color = "primary",
icon = icon("retweet")
)
)
),
shinyWidgets::pickerInput(
inputId = "zvar",
label = "Z Axis: ",
choices = my_vars,
options = list(
size = 5),
multiple = FALSE,
selected = "race"
)
)
)
#server.R
server <- function(input, output, session) {
#
observe({
if(!is.null(input$reverse_xz))
shinyWidgets::updatePickerInput(session, "zvar",
choices = my_vars[!(my_vars %in% input$xvar)],
selected = isolate(input$zvar) )
shinyWidgets::updatePickerInput(session, "xvar",
choices = my_vars[!(my_vars %in% input$zvar)],
selected = isolate(input$xvar) )
})
# These observers remove the selected choices so both pickers are unique
observe({
if(!is.null(input$zvar))
shinyWidgets::updatePickerInput(session, "xvar",
choices = my_vars[!(my_vars %in% input$zvar)],
selected = isolate(input$xvar) )
})
observe({
if(!is.null(input$xvar))
shinyWidgets::updatePickerInput(session, "zvar",
choices = my_vars[!(my_vars %in% input$xvar)],
selected = isolate(input$zvar) )
})
# output inputs
output$inputs <- renderText({ paste0("x var: ", input$xvar,
"\n\n\n z var:", input$zvar,
"\n\n\nreverse press: ", input$reverse_xz) })
}
shiny::shinyApp(ui= ui, server= server)
Thank you in advance. I have looked at some relavant posts however they couldn't guide me much:
Updatepickerinput with change in pickerinput in Shiny
updatePickerInput not updating values after changing tabs in R shiny
update pickerInput by using updatePickerInput in shiny
Look at this and check if it would be OK for you:
#global.R
library(shiny)
library(shinydashboard)
library(shinydashboardPlus)
library(shinyWidgets)
library(shinyjs)
#variable labels
my_vars <- c("None"= "NONE",
"All" = "all_all",
"Pro" = "Pro_",
"Locomania" = "locomania_Type",
"Racer" = "race")
#ui.R
ui <- shinydashboardPlus::dashboardPage(
header = shinydashboardPlus::dashboardHeader( ),
body = shinydashboard::dashboardBody( box(textOutput("inputs") ) ),
sidebar = shinydashboardPlus::dashboardSidebar(
shinyWidgets::pickerInput(
inputId = "xvar",
label = "X Axis: ",
choices = my_vars,
options = list(
size = 5),
multiple = FALSE,
selected = "all_all"
),
# Button to reverse the choices
shiny::fluidRow(
shiny::column(12, offset = 4,
shinyWidgets::actionBttn(
inputId = "reverse_xz",
label = "",
style = "simple",
color = "primary",
icon = icon("retweet")
)
)
),
shinyWidgets::pickerInput(
inputId = "zvar",
label = "Z Axis: ",
choices = my_vars,
options = list(
size = 5),
multiple = FALSE,
selected = "race"
)
)
)
#server.R
server <- function(input, output, session) {
#
observeEvent(input$reverse_xz, {
shinyWidgets::updatePickerInput(session, "zvar",
choices = my_vars[!(my_vars %in% input$zvar)],
selected = input$xvar)
shinyWidgets::updatePickerInput(session, "xvar",
choices = my_vars[!(my_vars %in% input$xvar)],
selected = input$zvar)
})
observe({
if (input$xvar == input$zvar && (length(input$zvar) > 0 && length(input$xvar) > 0)) {
shinyWidgets::updatePickerInput(session, "zvar",
selected = "")
shinyWidgets::updatePickerInput(session, "xvar",
selected = "")
}
})
# output inputs
output$inputs <- renderText({ paste0("x var: ", input$xvar,
"\n\n\n z var:", input$zvar,
"\n\n\nreverse press: ", input$reverse_xz) })
}
shiny::shinyApp(ui= ui, server= server)
I think that maybe this needs an explanation:
if (input$xvar == input$zvar && (length(input$zvar) > 0 && length(input$xvar) > 0))
So, when user choose two the same inputs, then we are updating pickerInputs, so both will have "Nothing selected" as a sign for user that something goes wrong (or that she/he did something wrong). However, "Nothing selected" is like NULL and we can't use NULL like this NULL == "something" inside if, so I'm checking if some input is NULL using length(input$) > 0, because length of NULL is 0. Instead of length(input$) > 0 you could use !is.null(input$) and maybe you should as it is probably more readable, but I'm leaving this decision for you.
I am currently developing a Shiny app.
I would like to use multiple Picker Inputs to branch the processing flow.
Specifically, I would like to build the processing flow shown in the figure below.
For example
When Option1 is selected in PickerInput1
→ PickerInput2 displays the vector of Middle Class.
→ Set the Multiple option of PickerInput2 to False to make it a single selection.
→ Turn off Numeric Input so that you cannot enter numbers.
(Make it gray out or hide it on the UI in the first place)
→ Press the button to display "expression1".
When Option2 is selected in PickerInput1
→ PickerInput2 displays the vector of Middle Class.
→ Set the Multiple option of PickerInput2 to False to make it a single selection.
→ Turn on Numeric Input so that you cannot enter numbers.
→ Press the button to display "expression1".
When Option3 is selected in PickerInput1
→ PickerInput2 displays the vector of Middle Class and Major Class.
→ Set the Multiple option of PickerInput2 to True to select multiple options.
→ Turn off Numeric Input so that you cannot enter numbers.
→ Press the button to display "expression2".
When Option4 is selected in PickerInput1
→ PickerInput2 displays the MajorClass vector.
→ Set the Multiple option of PickerInput2 to True to select multiple options.
→ Turn off Numeric Input so that you cannot enter numbers.
→ Press the button to display "expression2".
Is it possible to branch the processing flow in this way?
I especially want to hear
・ Is it possible to change the Mutiple settings?
(Maybe I will use updatePickerInput, but can I change it with that function?)
・ Can Numeric Input be switched ON / OFF?
The sample code is below.
library("shiny")
library("shinyWidgets")
major_class <- c("A1","A2","A3")
middle_class <- c("A1_1","A1_2","A2_1","A2_2","A3_1","A3_2")
ui <- fluidPage(
fluidRow(
column(
width = 2, offset = 1,
pickerInput(
inputId = "p1",
label = "PickerInput1",
choices = c("Option1","Option2","Option3","Option4"),
options = list(
`live-Search` = TRUE,
`actions-box` = TRUE,
size = 7,
`selected-text-format` = "count > 3"
),
multiple =FALSE
)
),
column(
width = 2,
pickerInput(
inputId = "p2",
label = "PickerInput2",
choices = "",
options = list(
`live-Search` = TRUE,
`actions-box` = TRUE,
size = 7,
`selected-text-format` = "count > 3"
),
multiple =TRUE
),
numericInput("num", label = "Numeric input", value = 1)
),
column(
width = 2,
actionButton("p3","finish")
),
textOutput("resule_message")
)
)
server <- function(input, output, session) {
observeEvent(input$p1 ,{
if(input$p1 %in% c("Option1","Option2")){
updatePickerInput(session = session, inputId = "p2",
choices = middle_class)
}
if(input$p1 %in% c("Option3")){
updatePickerInput(session = session, inputId = "p2",
choices = list(major = major_class,
middle = middle_class))
}
if(input$p1 %in% c("Option4")){
updatePickerInput(session = session, inputId = "p2",
choices = major_class)
}
})
observeEvent(input$p3 ,{
if(input$p1 %in% c("Option1")){
output$resule_message <- renderText({
paste0("expression1")
})
}
else{
output$resule_message <- renderText({
paste0("expression2")
})
}
})
}
shinyApp(ui = ui, server = server)
Those who can solve this problem, those who know how to change the processing flow depending on the input conditions of PikcerInput,
If you know that you can do something similar using another method, please let me know.
One way to do it is to use renderUI for your second pickerInput and display it based on your conditions. Try this
library("shiny")
library("shinyWidgets")
major_class <- c("A1","A2","A3")
middle_class <- c("A1_1","A1_2","A2_1","A2_2","A3_1","A3_2")
ui <- fluidPage(
fluidRow(
column(
width = 2, offset = 1,
pickerInput(
inputId = "p1",
label = "PickerInput1",
choices = c("Option1","Option2","Option3","Option4"),
options = list(
`live-Search` = TRUE,
`actions-box` = TRUE,
size = 7,
`selected-text-format` = "count > 3"
),
multiple =FALSE
)
),
column(
width = 2, uiOutput("pickr2"), uiOutput("numinput")
),
column(
width = 2,
actionButton("p3","finish")
),
textOutput("resule_message")
)
)
server <- function(input, output, session) {
output$pickr2 <- renderUI({
req(input$p1)
if(input$p1 %in% c("Option1","Option2")){
choices <- middle_class
TORF <- FALSE
}else{
TORF <- TRUE
if(input$p1 %in% c("Option3")){
choices <- c(middle_class,major_class)
}else choices <- major_class
}
pickerInput(
inputId = "p2",
label = "PickerInput2",
choices = choices,
options = list(
`live-Search` = TRUE,
`actions-box` = TRUE,
size = 7,
`selected-text-format` = "count > 3"
),
multiple =TORF
)
})
output$numinput <- renderUI({
req(input$p1)
if(input$p1 %in% c("Option2")){
numericInput("num", label = "Numeric input", value = 1)
}else return(NULL)
})
observeEvent(input$p3 ,{
req(input$p1)
if(input$p1 %in% c("Option1")){
output$resule_message <- renderText({
paste0("expression1")
})
}else{
output$resule_message <- renderText({
paste0("expression2")
})
}
})
}
shinyApp(ui = ui, server = server)
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'm writing a shiny app in which I want the user to be able to add and remove tabs. I know how to add tabs if the user clicks on a tab specifically made for that (thanks to the answer here) but I can't figure out how to remove the lastly created tab.
Here's a reproducible example:
library(shiny)
library(shinyWidgets)
ui <- navbarPage(position = "static-top",
title = "foo",
id = "tabs",
tabPanel(title = "Name 1",
fluidRow()),
tabPanel(title = "More",
icon = icon("plus"),
fluidRow()),
tabPanel(title = "Less",
icon = icon("minus"),
fluidRow())
)
server <- function(input, output) {
count <- reactiveVal(1)
observeEvent(input$tabs, {
if (input$tabs == "More"){
count(count()+1)
id = paste0("Name ", count())
insertTab(inputId = "tabs",
tabPanel(title = id,
fluidRow(column(
width = 12))
), target = "More", position = "before",
select = TRUE)}
if (input$tabs == "Less"){
count(count()+1)
id = paste0("Name ", count())
removeTab(inputId = "tabs",
target = id
)}
})
}
shinyApp(ui = ui, server = server)
Here, you can see that clicking on the tab More adds a tab named Name i with i the number of clicks made on the tab More. However, clicking on the tab Less does nothing.
What I would like is the following:
if the user clicks at least once on More, then clicking on Less removes the last created tab (therefore placed before More)
if the user does not click on More then clicking on Less does nothing
imagine that I click twice on More then there will be two additional tabs named Name 2 and Name 3. Clicking on Less will remove Name 3 but if I click again on More, the additional tab will be again called Name 3 (therefore clicking on Less should not prevent the re-use of the name of the tab removed).
Does anybody know how to do it?
I've done this with adding selectors and having a button remove then reuse the selector indices. To do this using reactive values that count each time the individual button is pressed or logging when an interaction is done.
Give the below code a try:
library(shiny)
library(shinyWidgets)
ui <- navbarPage(position = "static-top",
title = "foo",
id = "tabs",
tabPanel(title = "Name 1",
fluidRow()),
tabPanel(title = "More",
icon = icon("plus"),
fluidRow()),
tabPanel(title = "Less",
icon = icon("minus"),
fluidRow())
)
server <- function(input, output) {
count <- reactiveValues(value = 1)
observeEvent(input$tabs, {
if (input$tabs == "More"){
count$value <- count$value + 1
id = paste0("Name ", count$value)
insertTab(inputId = "tabs",
tabPanel(title = id,
fluidRow(column(
width = 12))
), target = "More", position = "before",
select = TRUE)}
if (input$tabs == "Less"){
print(count$value)
id = paste0("Name ", count$value)
removeTab(inputId = "tabs",
target = id
)
count$value <- count$value -1
}
})
}
shinyApp(ui = ui, server = server)
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!