I'm attempting to use InsertUI and updateSelectizeInput methods in the server function as part of my app, primarily since my list of choices is so large.
library(shiny)
baby_names <- babynames::babynames %>%
distinct(name) %>%
.[["name"]] %>%
sort()
ui <- fluidPage(
tags$div(id = 'placeholder')
)
server <- function(input, output, session) {
id = "babies"
insertUI(selector = '#placeholder',
ui = tags$div(list(
selectizeInput("babynames", label = "Baby Names!", multiple = TRUE, choices = NULL, width = '400px',
options = list(placeholder = 'Type a baby name.'))
),
immediate = TRUE,
id = id))
updateSelectizeInput(
session, inputId = "babynames",
choices = baby_names,
server = TRUE)
}
shinyApp(ui, server)
I'm not getting much success out of this, as the selectizeInput is displayed but the dropdown options are not shown. How should I address this issue? Thanks!
This is explained in the documentation of insertUI:
This function allows you to dynamically add an arbitrarily large UI object into your app, whenever you want, as many times as you want. Unlike renderUI(), the UI generated with insertUI is not updatable as a whole: once it's created, it stays there. Each new call to insertUI creates more UI objects, in addition to the ones already there (all independent from one another). To update a part of the UI (ex: an input object), you must use the appropriate render function or a customized reactive function
(I added the bold font)
Therefore, you should use renderUI instead.
Related
I have two pickerInput values in the app. In the first, geography, the user can select to view either state (default) or county data. If the user selects county, I'd like to require that they pick a state from the second pickerInput, which is just a list of states. It is not required that the user pick a state when input$geography == "state".
I have considered putting this inside of a modalDialogue but it wasn't working. I also tried an updatePickerInput which didn't work either.
What is the best way of conditionally requiring the user to select a value from a pickerInput?
Thank you.
Here is a solution with shinyjs :
library(shiny)
library(shinyWidgets)
library(shinyjs)
ui <- fluidPage(
useShinyjs(),
pickerInput("pick_1", choices = c("state","county"), multiple = FALSE, selected = "state"),
shinyjs::hidden(
pickerInput("pick_2", choices = state.name, multiple = TRUE)
)
)
server <- function(input, output, session) {
observe({
toggleElement("pick_2", condition = input$pick_1 == "state")
})
}
shinyApp(ui, server)
I am creating a shiny app and realized I am repeating a particular UI element so I am wondering if there is a way to wrap this in a function and supply parameters to make it work in different cases. In my server file, I have
output$loss <- renderUI({
req(input$got)
if(input$got %in% Years) return(numericInput('got_snow', label = 'John Snow', value = NA))
if(!input$got %in% Years) return(fluidRow(column(3)))
})
and in the ui file, I have:
splitLayout(
cellWidths = c("30%","70%"),
selectInput('got', label = 'Select age', choices = c('',Years) , selected = NULL),
uiOutput("loss")
)
Since I find myself using these several times and only changing a few things in both the UI and server files, I wanted to wrap these in a function and use them as and when I please. I tried this for the server file
ui_renderer <- function(in_put, label, id){
renderUI({
req(input[[in_put]])
if(input[[in_put]] %in% Years) return(numericInput(id, label = label, value = NA))
if(!input[[in_put]] %in% Years) return(fluidRow(column(3)))
})
}
output$p_li <- ui_renderer(input='li', "Enter age", id="c_li")
and in my ui file, I put
uiOutput('c_li')
but it's not working. Any help is greatly appreciated.
I was unable to test your code since there was no minimal working example. I don't know if this is a typo in your example, but your are trying to render c_li, but your output is called p_li. Not sure how wrapping a render object in a standard function works, but I have done something similar using reactive values instead.
This is a minimal example using some of your terminology. It is not a working example, but an outline of the idea to my proposed solution.
# Set up the UI ----
ui <- fluidPage(
uiOutput("loss")
)
# Set up the server side ----
server <- function(input, output, session) {
# Let us define reactive values for the function inputs
func <- reactiveValues(
value <- "got",
label <- "select age",
id <- "xyz"
)
# Set up an observer for when any of the reactive values change
observeEvent({
func$value
func$label
func$id
}, {
# Render a new UI any time a reactive value changes
output[["loss"]] <- renderUI(
if (input[[func$value]] %in% years) {
numericInput(func$id, label = func$label, value = NA)
} else {
fluidRow(
column(3)
)
}
)
})
}
# Combine into an app ----
shinyApp(ui = ui, server = server)
The general idea is to define a set of reactive values and set up an observer that will render the UI every time one or more of the reactive values change. You can assign a new value to any of the reactive values using direct assignment, e.g. current$value <- "object_two". Making that change will update the UI using Shiny's reactive pattern, which means you only need to change one value to update the UI.
I'm building my first Shiny app and I'm running into some trouble.
When I register a new user for the app, I need to add a row with some empty values to a dataframe, that will be used to generate recommendations.
user_features3 <- rbind(user_features3,c(612,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
This works alright while the app isn't running, and the 612th row is added. But while it's running, this has no effect on user_features3. I think this may be because R is busy with the webapp.
If I make a reactive value, say values <- reactiveValues() and then
value$df <- user_features3, I can modify the values in this reactive one, but I am unable to access the non-reactive one in a similar manner while the app is running.
I need to update it in real-time so that I can use it to generate movie recommendations. Any suggestions?
This solves your asked question, but modifying non-reactive variables can cause problems as well. In general, thinking about modifying a non-reactive variable within a shiny environment indicates perhaps you aren't thinking about how to scale or store or properly maintain the app state. For instance, if you are expecting multiple users, then know that this data is not shared with the other users, it is the current-user only. There is no way around this using local variables. (If you need to "share" something between users, you really need a data backend such as some form of SQL, including SQLite. See: https://shiny.rstudio.com/articles/persistent-data-storage.html)
In addition to all of the other shiny tutorials, I suggest you read about variable scope in shiny, specifically statements such as
"A read-only data set that will load once, when Shiny starts, and will be available to each user session", talking about data stored outside of the server function definition;
"This local copy of [this variable] is not be visible in other sessions", talking about a variable stored within the server function; and
"Objects defined in global.R are similar to those defined in app.R outside of the server function definition".
Having said that, two offered solutions.
Reactive Frame (encouraged!)
library(shiny)
ui <- fluidPage(
# App title ----
titlePanel("Hello Shiny!"),
sidebarLayout(
sidebarPanel(
textInput(inputId = "sometext", label = "Some Text:",
placeholder = "(nothing meaningful)"),
actionButton(inputId = "addnow", label = "Add!")
),
mainPanel(
tableOutput("myframe")
)
)
)
server <- function(input, output, session) {
rxframe <- reactiveVal(
data.frame(txt = character(0), i = integer(0),
stringsAsFactors = FALSE)
)
observeEvent(input$addnow, {
newdat <- data.frame(txt = isolate(input$sometext),
i = nrow(rxframe()) + 1L,
stringsAsFactors = FALSE)
rxframe( rbind(rxframe(), newdat, stringsAsFactors = FALSE) )
})
output$myframe <- shiny::renderTable( rxframe() )
}
shinyApp(ui, server)
This example uses shiny::reactiveVal, though it would be just as easy to use shiny::reactiveValues (if multiple reactive variables are being used).
Non-Reactive Frame (discouraged)
library(shiny)
ui <- fluidPage(
# App title ----
titlePanel("Hello Shiny!"),
sidebarLayout(
sidebarPanel(
textInput(inputId = "sometext", label = "Some Text:",
placeholder = "(nothing meaningful)"),
actionButton(inputId = "addnow", label = "Add!")
),
mainPanel(
tableOutput("myframe")
)
)
)
server <- function(input, output, session) {
nonrxframe <- data.frame(txt = character(0), i = integer(0),
stringsAsFactors = FALSE)
output$myframe <- shiny::renderTable({
req(input$addnow)
nonrxframe <<- rbind(nonrxframe,
data.frame(txt = isolate(input$sometext),
i = nrow(nonrxframe) + 1L,
stringsAsFactors = FALSE),
stringsAsFactors = FALSE)
nonrxframe
})
}
shinyApp(ui, server)
Both allow sequencing as the screenshots below demonstrate. Many will argue that the first (reactive) example is cleaner and safer. (Just the use of <<- is deterrent enough for me!)
I am building an Rshiny dashboard and am working to integrate some of shiny's more interactive features, and am currently working with the renderUI function, which (should, I believe) create additional widgets / input parameters based on the value of another input parameter. I am running into a simple error, but am having difficulty debugging. Below is a demo with the relevant code:
choices = c('All', 'None')
names(choices) = choices
ui <- fluidPage(theme = shinytheme('united'),
# create permanent input for shot chart type (should be 5 options)
selectInput(inputId = 'choice.input', label = 'Select a choice', multiple = FALSE,
choices = choices, selected = 'All'),
uiOutput('secondinput')
)
server <- shinyServer(function(input, output) {
if(input$choice.input == 'All') {
my.second.input <- c('a', 'b', 'c', 'd', 'e')
names(my.second.input) <- my.second.input
# player parameter for player whose shot chart will be shown
output$secondinput <- renderUI({
selectInput(inputId = 'another.input', label = 'Check this input', multiple = FALSE,
choices = my.second.input, selected = 'a')
})
}
})
shinyApp(ui, server)
I'm not sure what's wrong here - I thought my use of renderUI() in the server function, with the names matching (output$secondinput, uiOutput('secondinput')), was corrent, but this is throwing an error at me...
Note that my full code has several options for choice.input, and I would like to have an if() case in the server for each of the (4-5) choice.input() values. Any help with what's wrong here is appreciated, thanks!
EDIT - to clarify, the select input choice.input, with label 'Select a Choice', should be displaying always. When this input is set to 'All', then I'd like for an additional input, secondinput, to be displayed. If choice.input is not set to 'All', then I don't want the second input to be displayed. Hope this helps.
Here's a version of your code that works. I'm not sure if this is exactly what you want, it was a bit hard to tell, but hopefully you can take it from there.
choices = c('All', 'None')
names(choices) = choices
ui <- fluidPage(
# create permanent input for shot chart type (should be 5 options)
selectInput(inputId = 'choice.input', label = 'Select a choice', multiple = FALSE,
choices = choices, selected = 'All'),
uiOutput('secondinput')
)
server <- shinyServer(function(input, output) {
# player parameter for player whose shot chart will be shown
output$secondinput <- renderUI({
if(input$choice.input == 'All') {
my.second.input <- c('a', 'b', 'c', 'd', 'e')
names(my.second.input) <- my.second.input
selectInput(inputId = 'another.input', label = 'Check this input', multiple = FALSE,
choices = my.second.input, selected = 'a')
} else{
return(NULL)
}
})
})
shinyApp(ui, server)
The error that happened told you that you're trying to access a reactive value without a reactive context. You were trying to access the input value (which is reactive) outside of a render function or an observe function, which are reactive contexts.
If you don't understand what that means, I highly suggest you read this section on a shiny tutorial I wrote, reactivity 101.
The second issue here was that you were trying to "nest" a render function, which can work but is the wrong way to think about it, and suggests that you may not really grasp the concept of reactivity and render functions fully. Notice that I moved the render function to the outside, that's generally the correct way to program in shiny. If you have a bit of time, I suggest watching Joe Cheng's (the author of shiny) videos "effective reactive programming I and II" from the 2016 shiny conference https://www.rstudio.com/resources/webinars/shiny-developer-conference/
I have a shiny application with many tabs and many widgets on each tab. It is a data-driven application so the data is tied to every tab.
I can save the application using image.save() and create a .RData file for later use.
The issue I am having how can I get the state restored for the widgets?
If the user has checked boxes, selected radio buttons and specified base line values in list boxes can I set those within a load() step?
I have found libraries such as shinyURL and shinystore but is there a direct way to set the environment back to when the write.image was done?
I am not sure where to even start so I can't post code.
edit: this is a cross-post from the Shiny Google Group where other solutions have been suggested
This is a bit hacky, but it works. It uses an "internal" function (session$sendInputMessage) which is not meant to be called explicitly, so there is no guarantee this will always work.
You want to save all the values of the input object. I'm getting all the widgets using reactiveValuesToList(input) (note that this will also save the state of buttons, which doesn't entirely make sense). An alternative approach would be to enumerate exactly which widgets to save, but that solution would be less generic and you'd have to update it every time you add/remove an input. In the code below I simply save the values to a list called values, you can save that to file however you'd like (RDS/text file/whatever). Then the load button looks at that list and updates every input based on the value in the list.
There is a similar idea in this thread
library(shiny)
shinyApp(
ui = fluidPage(
textInput("text", "text", ""),
selectInput("select", "select", 1:5),
uiOutput("ui"),
actionButton("save", "Save"),
actionButton("load", "Load")
),
server = function(input, output, session) {
output$ui <- renderUI({
tagList(
numericInput("num", "num", 7),
checkboxGroupInput("chk", "chk", 1:5, c(2,4))
)
})
observeEvent(input$save, {
values <<- lapply(reactiveValuesToList(input), unclass)
})
observeEvent(input$load, {
if (exists("values")) {
lapply(names(values),
function(x) session$sendInputMessage(x, list(value = values[[x]]))
)
}
})
}
)
Now with bookmarking is possible to save the state of your shinyapp. You have to put the bookmarkButton on your app and also the enableBookmarking.
The above example may not work if shiny UI involves date. Here is a minor change for date handling.
library(shiny)
shinyApp(
ui = fluidPage(
dateInput("date", "date", "2012-01-01"),
selectInput("select", "select", 1:5),
uiOutput("ui"),
actionButton("save", "Save"),
actionButton("load", "Load")
),
server = function(input, output, session) {
output$ui <- renderUI({
tagList(
numericInput("num", "num", 7),
checkboxGroupInput("chk", "chk", 1:5, c(2,4))
)
})
observeEvent(input$save, {
values <<- lapply(reactiveValuesToList(input), unclass)
})
observeEvent(input$load, {
if (exists("values")) {
lapply(names(values),
function(x) session$sendInputMessage(x, list(value = values[[x]]))
)
temp=as.character(as.Date(values$date, origin = "1970-01-01"))
updateDateInput(session, inputId="date", label ="date", value = temp)
}
})
}
)