Basic R shiny local scoping object - r

I want a local variable in each session that can be updated by an input, which can be used by all other functions in the server. See the simple example below, I want the object to be updated when the user changes value but it doesn't?
library(shiny)
# Define UI for application
ui = shinyUI(pageWithSidebar(
# Application title
headerPanel("Hello Shiny!"),
# Sidebar with a slider input for data type
sidebarPanel(
selectInput("data",
"Pick letter to us in complex app?", choices = c("A","B"),
selected = "A")
),
# Print letter
mainPanel(
textOutput("Print")
)
))
server =shinyServer(function(input, output) {
MYLetter = "A";
updateData = reactive({
if (input$data == "A") {
MYLetter <<- "A"
} else {
MYLetter <<- "B"
}
})
output$Print <- renderText({
print(MYLetter)
})
})
shinyApp(ui, server)
I feel a solution will be global variables, but if two people are on the app at the same time. Will one person assigning a new value to a global variable change the variable for the other user?

There's a couple problems with your code. Here is the code that you want, I tried making very minimal changes to your code in order to make it work:
ui = shinyUI(pageWithSidebar(
# Application title
headerPanel("Hello Shiny!"),
# Sidebar with a slider input for data type
sidebarPanel(
selectInput("data",
"Pick letter to us in complex app?", choices = c("A","B"),
selected = "A")
),
# Print letter
mainPanel(
textOutput("Print")
)
))
server =shinyServer(function(input, output) {
MYLetter = reactiveVal("A");
observe({
if (input$data == "A") {
MYLetter("A")
} else {
MYLetter("B")
}
})
output$Print <- renderText({
print(MYLetter())
})
})
shinyApp(ui, server)
Essentially the two problems were:
What you are looking for is creating a reactive value with reactiveVal() or reactiveValues(). You're absolutely correct that creating a global variable is not the correct solution, because then it would be shared among all the users. It also is not reactive that way.
I changed the reactive({...}) to an observe({...}). It's very important to understand the difference between a reactive and an observer. I suggest reading online about it. I changed it to an observe because you weren't returning a value that was being used - rather, you were making an assignment within it.

Related

Shiny - Changing the number of choices in selectInput()

I want to change the number of choices in selectInput(). The following reprex works if the new choices are equal in number to the original choices, but if additional (or fewer) choices are offered, the code does not work. How can I get shiny to accept not just new choices for selectInput(), but a new number of choices? Thanks in advance for any help.
Philip
library(shiny)
ui <- fluidPage(
tabPanel("tbls",
selectInput("tab1",label="Pick a table:",choices=c("a","b","c")),
selectInput("cht1",label="Pick a time series:",choices=c("d","e","f"))
)
)
server <- function(input,output,session) {
Nchoices <- reactive({case_when(
input$tab1=="a" ~c("d","e","f"),
input$tab1=="b" ~c("g","h","i"),
input$tab1=="c" ~c("j","k","l","m") # adding one more choice breaks the code
)})
observe({updateSelectInput(session,"cht1",
label="Pick a time series:",choices=Nchoices(),selected=NULL)})
observe(print(Nchoices()))
}
shinyApp(ui, server)
Instead of case_when, try to use switch. Also, renderUI might be useful. Try this
library(shiny)
ui <- fluidPage(
tabPanel("tbls",
selectInput("tab1",label="Pick a table:",choices=c("a","b","c")),
uiOutput("myselect")
#selectInput("cht1",label="Pick a time series:",choices=c("d","e","f"))
)
)
server <- function(input,output,session) {
Nchoices <- reactive({
switch(input$tab1,
"a" = c("d","e","f"),
"b" = c("g","h"),
"c" = c("j","k","l","m") # adding one more choice breaks the code
)
})
output$myselect <- renderUI({
req(input$tab1)
selectInput("cht1",label="Pick a time series:",choices=Nchoices())
})
observe(print(Nchoices()))
}
shinyApp(ui, server)
Please note that in case_when All RHS values need to be of the same type. Inconsistent types will throw an error.

In R Shiny, how can you render outputs outside an observer where their names are reactives?

I have a Shiny app where I have a dynamically created tabsetPanel where each tab contains a table. I do not know how many tabs/tables will be created in each session by users. I understand that it is bad practice to put render* functions inside observe or observeEvent calls but I can't think of any other way to do this. A minimal example of what I'm trying to do is shown below, which just picks a data set randomly to display on a given tab. Essentially, I'm trying to figure out how to call my table renderers without putting them inside an observe. More generally, although I have read it is bad practice to do this, I would also appreciate an explanation of exactly why it's not a good thing to do:
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
numericInput("tabs", "Number of tabs", value = 5),
),
mainPanel(
uiOutput("mytabset")
)
)
)
server <- function(input, output) {
output$mytabset <- renderUI({
mytabs <- lapply(seq_len(input$tabs), function(x) {
tabPanel(
paste("Tab", x),
tableOutput(paste0("tab", x))
)
})
do.call(tabsetPanel, mytabs)
})
observe({
set.seed(1)
lapply(seq_len(input$tabs), function(x) {
output[[paste0("tab", x)]] <- renderTable({
sample(list(mtcars, iris, trees, cars), 1)
})
})
})
}
shinyApp(ui = ui, server = server)
I haven't used them in a while, but I think if you use modules, you can call them from outside of a reactive context, and won't need an observe..? :)

Use functions or loops to create shiny UI elements

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.

One reactive function to be displayed on two different pages interactively

I have an application which has 3 tabItems. I want to use a slider on second page to display same result on 3rd page interactively, i.e. if 2nd page slider changes then 3rd page slider should also change respectively.
I have a reactive function on server side
choose_segment <- reactive({
Multiple conditions for dropdown{Due to security cant share the exact code.}
})
and this choose_segment is refered in UI once and now i want to use it on the third page as well, but when i am calling the function on third page it is not displaying any thing on ui and also not giving any error.
in UI it is called inside UIoutput.
uiOutput(choose_segment())
My observations : I think as per my study we can not call one function directly twice, so what i am doing is i have made two different functions and calling same function from them, i.e.
output$chooseSegment1 <- renderUI({
choose_segment()
})
output$chooseSegment2 <- renderUI({
choose_segment()
})
Issue : it is giving me output but they both are not interactive :(
Kindly provide a solution so that i can make both the sliders work in interactive manner.
I have faced the same scenario, in that i was suppose to change the code structure.
I made dynamic output uiOutput to the Dropdown menu ob ui and then used the same in my server as Input$xyz in observe on server and it worked for me.
Code :
UI : column(3, selectInput(inputId="ABC",label= "Choose ABC"))
column(3, selectInput(inputId="ABC1",label= "Choose ABC"))
Server : observe({
if(is.null(tab2_summary())) return(NULL)
updateSelectInput(session, "ABC", value = input$ABC)
})
observe({
updateSelectInput(session, "ABC1", value = input$ABC)
})
observe({
updateSelectInput(session, "ABC", value = input$ABC1)
})
So this is how i was able to make the selectInput interactive on two different page.
For your reference there is one full reproducible code.
Kindly refer,
library(shiny)
# UI ----------------------------------------------------------
ui <- navbarPage("Navbar!",
tabPanel("Plot", sidebarLayout(sidebarPanel(
radioButtons("yaxis1", "y-axis", c("speed"="speed", "dist"="dist"),
selected = "speed"
)),
mainPanel( plotOutput("plot"),
textOutput("test2")))), # for input checking
tabPanel("Summary", sidebarLayout(sidebarPanel(
radioButtons("yaxis2", "grouping-var", c("speed"="speed", "dist"="dist")
)),
mainPanel(
verbatimTextOutput("summary"),
textOutput("test1")
)))
)
# Server ------------------------------------------
server <- function(input, output, session) {
observe({
x <- input$yaxis1
updateRadioButtons(session, "yaxis2", selected = x)
})
observe({
y <- input$yaxis2
updateRadioButtons(session, "yaxis1", selected = y)
})
# output$test1 <- renderPrint({cat("yaxis1", input$yaxis1)})
# output$test2 <- renderPrint({cat("yaxis2", input$yaxis2)})
# output$plot <- renderPlot({ plot(cars[['speed']], cars[[input$yaxis1]]) })
# output$summary <- renderPrint({ summary(cars[[input$yaxis2]]) })
}
shinyApp(ui, server)
I Hope it will of your help.

R Shiny - how to force submit button to affect not all inputs

I am facing a problem with submitButton usage in my Shiny application (which I use as some time-consuming rendering is done with the data supplied by the app-user). I also use some radioButtons with conditionalPanel to define the variables group of which user may choose the parameters. Please see the attached image to get the idea (user is selecting a list type, and - based on his list choice - a particular list appears (conditionalPanel is working) from which the user is selecting a parameter), or run a reprodicible example supplied below.
Of course, only the parameter is a value that is using in rendering an output, and I would like to force a submitButton to pass only the parameter in an automatic way. The problem is that submitButton affects also the radioButtons element, which unables the use to choose the desired value (as the values list are not switching).
QUESTION: Is there any way to define which UI elements are to be stop-from-automatic-update by submitButton so as to solve my problem? Thank you for any help!
UI:
library(shiny)
shinyUI(pageWithSidebar(
headerPanel("Problem with submit button"),
sidebarPanel(
radioButtons("selectionway", "Choose list type", c(number='number', letter='letter')),
conditionalPanel(
condition = "input.selectionway == 'number'",
selectInput("numberlist", "Choose NUMBER:", choices = c("11111", "22222", "33333"))
),
conditionalPanel(
condition = "input.selectionway == 'letter'",
selectInput("letterlist", "Choose LETTER:", choices = c("A", "B", "C"))
),
submitButton("submitButton")
),
mainPanel(
verbatimTextOutput("list"),
verbatimTextOutput("value")
)
))
SERVER:
library(shiny)
shinyServer(function(input, output) {
selected.value <- reactive({
if(input$selectionway=="letter"){
return(input$letterlist)
} else {
return(input$numberlist)
}
})
output$list <- renderText({
input$selectionway
})
output$value <- renderText({
selected.value()
})
})

Resources