Impossible to access input from within R Shiny UI module? - r

I want to have the choices in a radioButtons input depend on the selection of a prior input, and I would like to wrap both inputs up in a UI module. I know that passing the inputs of one module to another requires passing to the server first. But I can't seem to access an input within the same UI.
Here's what I have in a single app.r (things will eventually get repeated over several tabs, thus the desire for a module, but we're keeping it simple for now):
library(shiny)
# Sidebar selector UI module
sidebarSel_ui <- function(id){
ns <- NS(id)
# conditional choices for secondInput based on firstInput
input2Choices <- if (ns(input$firstInput == 1)) { # having trouble here
list(
"Choice A"=1,
"Choice B"=2
)
} else {
list(
"Choice C"=1,
"Choice D"=2
)
}
# inputs
tagList(
radioButtons(
inputId=ns("firstInput"),
label="Choose one:",
choices=list(
"Choice 1"=1,
"Choice 2"=2),
selected=1
),
radioButtons(
inputId=ns("secondInput"),
label="Choose another",
choices=input2Choices,
selected=1
)
)
}
# Define UI
ui <- fluidPage(
# Sidebar
sidebarLayout(
sidebarPanel(
sidebarSel_ui("test")
),
# Main panel
mainPanel(
)
)
)
# Define server
server <- function(input, output, session) {
}
# Run the application
shinyApp(ui = ui, server = server)
I'm getting an "object 'input' not found" error. If I do actually have to split these inputs into different modules, send the results of the first to the server and then send that back to the second to make it work, I think I can figure that out. It seems odd to not be able to access inputs within the same UI.

I figured it out thanks to the hint from #Limey:
library(shiny)
# Sidebar selector UI module
sidebarSel_ui <- function(id){
ns <- NS(id)
# inputs
tagList(
radioButtons(
inputId=ns("firstInput"),
label="Choose one:",
choices=list(
"Choice 1"=1,
"Choice 2"=2),
selected=1
),
radioButtons(
inputId=ns("secondInput"),
label="Choose another",
choices=list(
"Choice A"=1,
"Choice B"=2
),
selected=1
)
)
}
# Sidebar selector server module
sidebarSel_server <- function(id){
moduleServer(
id,
function(input, output, session){
observeEvent(input$firstInput,{
# conditional choices for input2 based on input1
input2Choices <- if (input$firstInput == 1) {
list(
"Choice A"=1,
"Choice B"=2
)
} else {
list(
"Choice C"=1,
"Choice D"=2
)
}
updateRadioButtons(session, "secondInput", choices=input2Choices)
})
})
}
# Define UI
ui <- fluidPage(
# Sidebar
sidebarLayout(
sidebarPanel(
sidebarSel_ui("test")
),
# Main panel
mainPanel(
)
)
)
# Define server
server <- function(input, output, session) {
sidebarSel_server("test")
}
# Run the application
shinyApp(ui = ui, server = server)
Key changes:
set up the second radioButtons input as usual with the first set of choices
set up an observeEvent in the module server that handles the if logic and
spits the choices list back with updateRadioButtons
call the module server in the main server function
More roundabout than I was expecting, and it looks like one cannot, in fact, refer to the inputs of a module ui within the module ui itself. The trick is to do the work in the module server and kick it back to the ui.

Related

Declaring uiOutput widgets on unopened tabs

I'm creating an R Shiny app that runs a function based off of many reactive widgets on multiple tabs. A problem I'm running into is that when the app is initially launched, I need to select the tabs that contain the respective reactive widgets before the widget's id is recognized in the "input". Is there a way to either 1.) have the app recognize all the reactive widgets when the app is deployed or 2.) alter the "input" initially to contain initial values for the reactive widget ids. Here is a simple example of the problem:
ui <- shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
tabsetPanel(
tabPanel("tab 1", numericInput("a", "A", 2)),
tabPanel("tab 2", uiOutput("bUI"))
)
),
mainPanel(
verbatimTextOutput("a_b")
)
)
))
server <- function(input, output, session){
output$bUI <- renderUI({
numericInput("b", "B", 3)
})
output$a_b <- renderPrint(input$a * input$b)
}
shinyApp(ui = ui, server = server)
I initially get an output of interger(0) instead of the desired 6.
Note, I do not want to require the user to have to select all the tabs. i.e. I don't want to use req or validate to push the user to click through the tabs.
I added an intermediate check if input$b is truthy. If not, a default 3 is used:
library('shiny')
ui <- shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
tabsetPanel(
tabPanel("tab 1", numericInput("a", "A", 2)),
tabPanel("tab 2", uiOutput("bUI"))
)
),
mainPanel(
verbatimTextOutput("a_b")
)
)
))
server <- function(input, output, session){
output$bUI <- renderUI({
numericInput("b", "B", 3)
})
safe_b <- eventReactive(input$b, if(isTruthy(input$b)) input$b else 3, ignoreNULL = F)
output$a_b <- renderPrint(input$a * safe_b())
}
shinyApp(ui = ui, server = server)

Updating selecInput with more than 1,000 items in Shiny

I want to update a selectInput item on a Shiny app with more than 1,000 items but it obviously don't accept more than 1,000.
Is there a method to add more values or load it from server, as user start typing? server parameter also doesn't work.
library(shiny)
# Define UI for application that draws a histogram
ui <- fluidPage(
tags$head(tags$script(src = "message-handler.js")),
# Application title
titlePanel("Large selectInput"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
selectInput("Names",
"List of Names",
choices = c("A")
)
),
mainPanel("Empty")
)
)
# Define server logic required to draw a histogram
server <- function(input, output, session) {
names <- 1:5000
observe({
updateSelectInput(session, "Names", label = "Updated", choices = names, server = TRUE)
})
}
# Run the application
shinyApp(ui = ui, server = server)
selectizeInput() can handle more than 1,000 records.

How to stop restarting list in selectize input after click in Shiny?

When I filter a list of states in Shiny for: "New" I can choose only one state. After that the list is restarting and I have to put: "New" again in order to find a state contains "New" in name. I would like to filter states and choose more states at one time.
Below I added a picture and a code which present my goal.
Picture
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectizeInput(
'e2', '2. Multi-select', choices = state.name, multiple = TRUE
)
),
mainPanel(
verbatimTextOutput('ex_out')
)
)
)
server <- function(input, output) {
output$ex_out <- renderPrint({
sapply(sprintf('e%d', 2), function(id) {
input[[id]]
})
})
}
shinyApp(ui = ui, server = server)

R Shiny conditionalPanel odd behavior

I'm using conditionalPanel to create a UI that first presents a panel with options to the user and then displays a tabbed dashboard using tabsetPanel. The simple act of adding another tabPabel as shown below somehow prevents the server.R file from running. I've tested using print statements. It seems like the Shiny app is breaking, but I can't find a syntax error or any reason for it to be.
conditionalPanel(
condition = "output.panel == 'view.data'",
tabsetPanel(id = "type",
tabPanel("Script", value = "script",
fluidPage(
br(),
fluidRow(
column(3, uiOutput("script.name")),
column(3, uiOutput("script.message"))
),
hr(),
plotlyOutput("plotly")
)
),
tabPanel("Location", value = "location",
fluidPage(
br(),
fluidRow(
# column(3, uiOutput("id.range"))
),
hr(),
plotlyOutput("plot")
)
)
# when this tabPanel is uncommented it doesn't work
# ,tabPanel("Accelerometer", value = "accelerometer",
# fluidPage(
# br(),
# hr(),
# plotlyOutput("plot")
# )
# ),
)
)
It's not failing because of the additional tabPanel, it's failing because it contains a duplicate reference to output$plot. Each output of the server function can only be displayed once. For example, this runs, but will fail if the duplicated line is uncommented:
library(shiny)
ui <- shinyUI(fluidPage(
# textOutput('some_text'),
textOutput('some_text')
))
server <- shinyServer(function(input, output){
output$some_text <- renderText('hello world!')
})
runApp(shinyApp(ui, server))
A simple solution is to save the results of the render* function to a local variable, which you can then save to two outputs:
library(shiny)
ui <- shinyUI(fluidPage(
textOutput('some_text'),
textOutput('some_text2')
))
server <- shinyServer(function(input, output){
the_text <- renderText('hello world!')
output$some_text <- the_text
output$some_text2 <- the_text
})
runApp(shinyApp(ui, server))

R Shiny: Nested tabPanels disable each other

In the attached MWE Shiny example, I have a nested tabsetPanel within a tabPanel for a navbar. If you run the MWE with only one tabPanel within the tabSet you will see that Shiny behaves exactly as it is expected. However, if you run the MWE with two tabPanels, the result is not printed to the main panel of each tab.
Why does this behaviour occur? And how do I resolve this conundrum?
library(shiny)
ui <- shinyUI(navbarPage("tabalicious",
tabPanel("Nav1", value = "nav1",
mainPanel(h2("Hello"),
br(),
p("This is my app.")
)
)
,
tabPanel("Nav2", value = "nav2",
tabsetPanel(
tabPanel("tabsettab1",
sidebarLayout(
sidebarPanel(
helpText("Choose your settings"),
selectInput("zone_type",
label = "Choose a zone type to display",
choices = list("Industrial", "Residential"),
selected = "Industrial")
),
mainPanel(h2("A tab for a tabSet"),
textOutput('zone_type')
)
)
)
# Uncomment this to see the issue
# ,
# tabPanel("tabsettab2",
# sidebarLayout(
# sidebarPanel(
# helpText("Choose your settings"),
# selectInput("zone_type",
# label = "Choose a zone type to display",
# choices = list("Industrial", "Residential"),
# selected = "Industrial")
# ),
# mainPanel(h2("A tab for a tabSet"),
# textOutput('zone_type')
# )
# )
# )
)
)
)
)
server <- shinyServer(function(input, output) {
output$zone_type <- renderText({
paste("You have selected", input$zone_type)
})
})
# Run the application
shinyApp(ui = ui, server = server)
It doesn't have to do with tabs, but multiple calls to output the results of the same render* function. For example, a simplified page (with no tabs) will work fine, but if you uncomment the duplicated call, will fail to display zone_type:
library(shiny)
server <- shinyServer(function(input, output) {
output$zone_type <- renderText({paste("You have selected", input$zone_type)})
})
ui <- shinyUI(fluidPage(
selectInput("zone_type",
label = "Choose a zone type to display",
choices = list("Industrial", "Residential")),
# textOutput('zone_type'),
textOutput('zone_type')
))
runApp(shinyApp(server = server, ui = ui))
While your shinyUI function can only call each output of shinyServer once, within shinyServer you can call the inputs as many times as you like, so it's easy to duplicate outputs:
library(shiny)
server <- shinyServer(function(input, output) {
output$zone_type <- renderText({paste("You have selected", input$zone_type)})
output$zone_type2 <- renderText({paste("You have selected", input$zone_type)})
})
ui <- shinyUI(fluidPage(
selectInput("zone_type",
label = "Choose a zone type to display",
choices = list("Industrial", "Residential")),
textOutput('zone_type'),
textOutput('zone_type2')
))
runApp(shinyApp(server = server, ui = ui))
If you don't want to duplicate work for the server, you can't pass one output to another, but you can just save the render* results to a local variable which you can pass to both outputs:
server <- shinyServer(function(input, output) {
zone <- renderText({paste("You have selected", input$zone_type)})
output$zone_type <- zone
output$zone_type2 <- zone
})

Resources