I have a Shiny app which contains two dataframes.
The user presses a button which creates new columns in the dataframe
I have a further selectInput box which I want to populate with the names of the dataframe columns as they appear in the Shiny app (ie if the user have pressed the button then the SlectInput box would contain the new column names).
My attempt so far:
---
title: "GUI"
output: html_document
runtime: shiny
---
```{r, echo=FALSE}
library(EndoMineR)
RV <- reactiveValues(data = mtcars)
shinyApp(
ui = fluidPage(
selectInput("variable", "Variable:",
colnames(RV$data)),
tableOutput("data")
),
server = function(input, output) {
observeEvent(input$doExtractor, {
mtcars$cyl2<-mtcars$cyl*10
})
}
)
```
but I get the error:
Operation not allowed without an active reactive context. (You tried to do something that can only be done from inside a reactive expression or observer.)
besides what MLavoie already mentioned, you want to use updateSelectInput for things like this. Below is working example.
shinyApp(
ui = fluidPage(
column(
width = 3,
selectInput(
inputId = "variable",
label = "Variable:",
choices = colnames(mtcars)),
actionButton(
inputId = "doExtractor",
label = "do Extractor"
)
),
column(
width = 9,
dataTableOutput("data")
)
),
server = function(input, output,session) {
RV <- reactiveValues(data = mtcars)
observeEvent(input$doExtractor, {
browser()
RV$data$cyl2<-mtcars$cyl*10
})
observe(
updateSelectInput(
session = session,
inputId = "variable",
choices = colnames(RV$data),
selected = input$variable # only necessary if you want to keep the selection
)
)
output$data <- renderDataTable({
browser()
DT::datatable(RV$data)
})
}
)
Hope this helps!
Related
I've created a SelectizeInput() UI using the insertUI() function. Essentially, I have an action button which adds a SelectizeInput() every time it's clicked. The idea is that the user selects columns from their data to put into groups. The creation of the UI works fine. I can also see in the Shiny trace that the selection works fine. However, I'm unsure how to access these variables to use in later plots. This is the code I have:
UI:
actionButton("cr_exp", "Create new biological group")
Server:
observeEvent(input$cr_exp, {
insertUI(
selector = "#cr_exp",
where = "afterEnd",
ui = selectizeInput(inputId = paste0("grp", input$cr_exp), label = "Select samples", choices = colnames(exp_dff()), options = list(create=TRUE), multiple=TRUE))
tags$div(id = paste0("grp", input$cr_exp))
})
In the shiny trace, it shows that the group is created, but I can't figure out how to access the value:
RECV {"method":"update","data":{"grp1":["MV4negControl01","MV4negControl02"]}}
You access the values just like any other input values: by the input element’s
id from the input reactive values:
library(shiny)
ui <- fluidPage(
actionButton("cr_exp", "Create new biological group"),
verbatimTextOutput("choices")
)
server <- function(input, output, session) {
observeEvent(input$cr_exp, {
insertUI(
selector = "#cr_exp",
where = "afterEnd",
ui = selectizeInput(
inputId = paste0("grp", input$cr_exp),
label = "Select samples",
choices = LETTERS,
options = list(create = TRUE),
multiple = TRUE
)
)
tags$div(id = paste0("grp", input$cr_exp))
})
output$choices <- renderPrint({
lapply(seq_len(input$cr_exp), function(i) input[[paste0("grp", i)]])
})
}
shinyApp(ui, server)
I have the following app which allows for text to be entered and it is then saved as VALUE and printed on a panel.
Although it looks like I can only do this with one text input at a time - even if I click add (so I don't believe this button is working). On top of that I would like for the user to be able to add multiple inputs (like I have below).
And then my VALUE function should be list with multiple inputs.
code below
library(shiny)
ui <- fluidPage(
headerPanel("R Package App"),
sidebarPanel(
# selectInput("options", "options", choices=c('abc','def')),
textInput("textbox", "Enter R Package Name", ""),
actionButton("add","Add")
),
mainPanel(
textOutput("caption")
)
)
server <- function(input, output, session) {
observe({
VALUE <- ''
if(input$add>0) {
isolate({
VALUE <- input$textbox
})
}
updateTextInput(session, inputId = "textbox", value = VALUE)
})
output$caption <- renderText({
input$textbox
})
}
shinyApp(ui = ui, server = server)
Have you considered using selectizeInput with it's create option?
library(shiny)
packagesDF <- as.data.frame(installed.packages())
ui <- fluidPage(
headerPanel("R Package App"),
sidebarPanel(
selectizeInput(
inputId = "selectedPackages",
label = "Enter R Package Name",
choices = packagesDF$Package,
selected = NULL,
multiple = TRUE,
width = "100%",
options = list(
'plugins' = list('remove_button'),
'create' = TRUE,
'persist' = TRUE
)
)
),
mainPanel(textOutput("caption"))
)
server <- function(input, output, session) {
output$caption <- renderText({
paste0(input$selectedPackages, collapse = ", ")
})
}
shinyApp(ui = ui, server = server)
The purpose is to choose a county from a state. I first create a selectInput widget for choosing a state. Then I create a selectInput widget for choosing a county from the selected state. In an R Markdown, the code is as follows:
inputPanel(
selectInput(inputId = "State", label = "Choose a state:", choices = state.name),
selectInput(inputId = "County", label = "Choose a county:", choices = input.State)
)
I guess the use of input.State is problematic, but I don't have any other idea.
Thanks for your time!
There are a number of ways to create conditional/dynamic UI in Shiny (see here). The most straightforward is usually renderUI. See below for a possible solution for you. Note that this requires Shiny so if you’re using R Markdown make sure to specify runtime: shiny in the YAML header.
library(shiny)
# I don't have a list of all counties, so creating an example:
county.name = lapply(
1:length(state.name),
function(i) {
sprintf("%s-County-%i",state.abb[i],1:5)
}
)
names(county.name) = state.name
shinyApp(
# --- User Interface --- #
ui = fluidPage(
sidebarPanel(
selectInput(inputId = "state", label = "Choose a state:", choices = state.name),
uiOutput("county")
),
mainPanel(
textOutput("choice")
)
),
# --- Server logic --- #
server = function(input, output) {
output$county = renderUI({
req(input$state) # this makes sure Shiny waits until input$state has been supplied. Avoids nasty error messages
selectInput(
inputId = "county", label = "Choose a county:", choices = county.name[[input$state]] # condition on the state
)
})
output$choice = renderText({
req(input$state, input$county)
sprintf("You've chosen %s in %s",
input$county,
input$state)
})
}
)
Hope this helps!
I built a R Shiny application with multiple tabs, which have some filters in common. Right now, all filters are stand-alone and do not synchronize across multiple tabs. Hence, when I change selectInput1 from value "a" to value "b", I have to repeat this handling on the next tab which contains selectInput2 with the same options/meaning.
I thought about making the filters dynamic, hence rendering them using the server side of R Shiny. Then of course, I can always make selectInput2 equal to selectInput1. But what if the user changes selectInput2 rather than selectInput1? It creates kind of a loop in the logic.
I spent quite some time finding a solution for this problem, and somehow I'm sure I'm not the first one encountering this problem. Suggestions or useful links would be really helpful!
Example:
## UI.R
shinyUI(
dashboardPage("Dashboard",
# Create tabs
tabPanel("Start",
p("This is the frontpage")
),
tabPanel("tab1",
uiOutput("selectInput1")
),
tabPanel("tab2",
uiOutput("selectInput2")
)
)
)
and:
## Server.R
library(shiny)
shinyServer(function(input, output,session){
output$selectInput1 <- renderUI({
selectInput(inputId = "id1",
label = "select",
choices = c("a","b","c"),
selected = "a")
})
output$selectInput2 <- renderUI({
selectInput(inputId = "id2",
label = "select",
choices = c("a","b","c"),
selected = "a")
})
})
I would personally use a single input control to control the different tab panels. One way is to include that single input under your tabs:
shinyApp(
fluidPage(
fluidRow(
tabsetPanel(
tabPanel("Tab1",
verbatimTextOutput("choice1")),
tabPanel("Tab2",
verbatimTextOutput("choice2"))
)
),
fluidRow(
selectInput("id1", "Pick something",
choices = c("a","b","c"),
selected = "a")
)
),
function(input, output, session){
output$choice1 <- renderPrint(input$id1)
output$choice2 <- renderPrint({
paste("The choice is:", input$id1)
})
}
)
Or, as you use a shinydashboard, you could actually add that control in the sidebar, possibly again in its own row under a set of tabs if you must.
I can't think of a reason to have multiple inputs who automatigically select the same thing. Other than slowing down your app, I can't see any gain. But if you insist, you make the selected choice a reactive value using reactiveVal and you use eg observeEvent() to update that reactive value. A small example using shinydashboard:
library(shinydashboard)
library(shiny)
ui <- shinyUI(
dashboardPage(title = "Dashboard",
dashboardHeader(),
dashboardSidebar(
tabsetPanel(
tabPanel("tab1",
uiOutput("selectInput1")
),
tabPanel("tab2",
uiOutput("selectInput2")
)
)),
dashboardBody(
verbatimTextOutput("selected")
)
)
)
server <- shinyServer(function(input, output,session){
thechoice <- reactiveVal("a")
output$selectInput1 <- renderUI({
selectInput(inputId = "id1",
label = "select",
choices = c("a","b","c"),
selected = thechoice())
})
output$selectInput2 <- renderUI({
selectInput(inputId = "id2",
label = "select",
choices = c("a","b","c"),
selected = thechoice())
})
observeEvent(input$id2,{
thechoice(input$id2)
})
observeEvent(input$id1,{
thechoice(input$id1)
})
output$selected <- renderPrint({
c(input$id1, input$id2)
})
})
shinyApp(ui, server)
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
})