Multiple reactiveValues In a Shiny App - r

I use reactiveValues in Shiny a lot as they are more flexible than just the input and output objects. Nested reactiveValues are tricky since any changes in any of the children also triggers the reactivity linked to the parents. To get around this, I tried to make two different reactiveValues objects ( not two objects in the same list, but two different lists altogether ) and it seems to be working. I'm not able to find any example of this and want to find out if it's suppose to work this way. Are there any issues that might arise because of this?
In this app, there are two reactive values objects - reac1 and reac2. Each of them are linked to a drop down, column1 and column2 respectively. Changing column1 or column2 updates the reactive values with the latest time, updates the plot, and prints the latest values in reac1 and reac2.
ui = fluidPage(
titlePanel("Multiple reactive values"),
sidebarLayout(
sidebarPanel(
selectInput(inputId = "column1", "Reac1", letters, selected = "a"),
selectInput(inputId = "column2", "Reac2", letters, selected = "a")
),
mainPanel(
plotOutput("plot1")
)
)
)
server = function(input, output, session) {
reac1 <- reactiveValues(asdasd = 0)
reac2 <- reactiveValues(qweqwe = 0)
# If any inputs are changed, set the redraw parameter to FALSE
observe({
input$column2
reac2$qweqwe = Sys.time()
})
observe({
input$column1
reac1$asdasd = Sys.time()
})
# Only triggered when the copies of the inputs in reac are updated
# by the code above
output$plot1 <- renderPlot({
print(paste(reac1$asdasd, 'reac1'))
print(paste(reac2$qweqwe, 'reac2'))
hist(runif(1000))
})
}
shinyApp(ui, server)

ReactiveValues are like a read/write version of input$, and you can have several 'independent' variables inside one reactiveValue list. So, you do not need two reactive values in your example. See code below.
ui = fluidPage(
titlePanel("Multiple reactive values"),
sidebarLayout(
sidebarPanel(
selectInput(inputId = "column1", "Reac1", letters, selected = "a"),
selectInput(inputId = "column2", "Reac2", letters, selected = "a")
),
mainPanel(
verbatimTextOutput("txt1"),
verbatimTextOutput("txt2")
)
)
)
server = function(input, output, session) {
reac <- reactiveValues()
#reac2 <- reactiveValues(qweqwe = 0)
# If any inputs are changed, set the redraw parameter to FALSE
observe({
reac$asdasd = input$column1
})
observe({
reac$qweqwe = input$column2
})
# Only triggered when the copies of the inputs in reac are updated
# by the code above
output$txt1 <- renderPrint({
print('output 1')
print(paste(reac$asdasd, 'reac1'))
})
output$txt2 <- renderPrint({
print('output2')
print(paste(reac$qweqwe, 'reac2'))
})
}
shinyApp(ui, server)

Related

Need help making dependent dropdown boxes in the RStudio package Shiny

I have two datasets, one with a list of two hundred cities and their corresponding state and another much larger dataset that I'd like to make an app to sort through. I need help making two drop down boxes in my shiny app where the first is the state variable and the second is the list of cities within that chosen state. I then want those selections to filter the much larger, second dataset in the output. I've tried solutions from several similar but slightly different examples online, but I'm having trouble translating it to what I'm doing.
So far I have this:
ui <- fluidPage(
headerPanel(''),
sidebarPanel(
#add selectinput boxs
htmlOutput("state_selector"),
htmlOutput("city_selector"),
),
mainPanel(
fluidRow(
# Create a new row for the table.
DT::dataTableOutput("table")
)
server <- function(session, input, output) {
output$state_selector = renderUI({
selectInput("state", label = h4("State"),
choices = as.character(unique(citystatedata$state)), selected = NULL)
})
output$city_selector = renderUI({
data_available = citystatedata[citystatedata$State == input$state, "state"]
selectInput(inputId = "city", #name of input
label = "City", #label displayed in ui
choices = unique(data_available), #calls list of available cities
selected = unique(data_available)[1])
})
shinyApp(ui = ui, server = server)
I tried to take out the portions of the code that weren't specifically related to the drop down boxes, since that's what I was more specifically asking about. So I'm sorry if I've left anything out! Let me know if I need to include anything else
Using available gapminder data, you can try this.
df <- gapminder
df$state <- gapminder$continent
df$city <- gapminder$country
citystatedata <- df
ui <- fluidPage(
headerPanel('Test'),
sidebarPanel(
#add selectinput boxs
uiOutput("state_selector"),
uiOutput("city_selector"),
),
mainPanel(
fluidRow(
# Create a new row for the table.
DTOutput("table")
)
)
)
server <- function(session, input, output) {
output$state_selector = renderUI({
selectInput("state", label = h4("State"),
choices = as.character(unique(citystatedata$state)), selected = NULL)
})
output$city_selector = renderUI({
data_available = citystatedata[citystatedata$state == req(input$state),]
selectInput(inputId = "city", #name of input
label = "City", #label displayed in ui
choices = unique(data_available$city), #calls list of available cities
selected = 1)
})
mydt <- reactive({
citystatedata %>% filter(citystatedata$state == req(input$state) & citystatedata$city %in% req(input$city))
})
output$table <- renderDT(mydt())
}
shinyApp(ui = ui, server = server)

RShiny: Refer to data from UI in server

Example Case: I have a function in my global.R called get_data which returns a list of many items. The reason I don't just put the data in global is so the data can automatically refresh after a certain amount of time
ui.R
my_data <- uiOutput("data") # Doesn't work
### Some more generic manipulation before final use
# The output of my_data will look like the following below.
my_data <- list()
my_data$first_entry <- c("a", "b", "d")
my_data$second_entry <- c("x", "y", "z") # and so on
shinyUI(navbarPage(theme=shinytheme("flatly"),
'App Name',
tabPanel('Title',
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
width=3,
# new box
checkboxGroupButtons(
'name',
'label:',
choices = sort(my_data$first_value),
status = 'primary',
selected = sort(my_data$first_value)[1],
size = 'xs'
# inline = TRUE
))
server.R
shinyServer(function(input, output, session) {
data <- reactive({
invalidateLater(100000,session)
get_data()
})
output$data <- renderUI({
data()
})
})
Two questions:
Is there any way of referencing my_data correctly?
If my function get_data is simply reading a (large) csv which is updated systematically. Is there a better way of doing it than I am currently doing it?
I think you're wondering how to define possible choices= for something within the UI element, when the data is both (1) undefined at the start, and (2) changing periodically. The answer to that is to define it "empty" and update it as the new data is found.
library(shiny)
library(shinyWidgets)
get_data <- function() as.list(mtcars[sample(nrow(mtcars), size=3), sample(ncol(mtcars), size=3)])
logg <- function(...) message(paste0("[", format(Sys.time()), "] ", ...))
shinyApp(
ui = fluidPage(
title = "Hello",
checkboxGroupButtons(inputId = "cb", label = "label:", choices = c("unk"), selected = NULL,
status = "primary", size = "xs"),
br(),
textOutput("txt"),
br(),
textAreaInput("txtarea", NULL, rows = 4)
),
server = function(input, output, session) {
data <- reactive({
logg("in 'data'")
invalidateLater(3000, session)
get_data()
})
observe({
logg("in 'observe'")
req(length(data()) > 0)
updateCheckboxGroupButtons(session = session, inputId = "cb", choices = names(data()))
updateTextAreaInput(session, "txtarea", value = paste(capture.output(str(data())), collapse = "\n"))
})
output$txt <- renderPrint({
logg("in 'txt'")
req(length(data()) > 0)
str(data())
})
}
)
Notice that the definition of checkboxGroupButtons starts with no real choices. I'd prefer to start it empty, but unlike selectInput and similar functions, it does not like starting with an empty vector. It is quickly (nearly-immediately) changed, so I do not see "unk" in the interface.
I demoed two options for "displaying" the data in its raw form: as an output "txt", and as an updatable input "txtarea". I like the latter because it deals well with fixed-width, but it requires an update* function (which is really not a big deal).

How to Set Values in selectInput R Shiny for a long set of options

I am creating an R Shiny app where I have an extremely long list of options for selectInput. Depending on the option you select, the value is going to change. I know that for a small list of options you can set the values yourself in the server function like so:
server <- function(input, output) {
output$graph <- renderPlot({
player <- switch(input$var,
"LeBron James" = 23,
"Kobe Bryant" = 24,
"DeMar DeRozan" = 10,
"Kyle Lowry" = 7)
plotGraph(player)
})
}
But my list has at least 100 options and it's certainly not clean nor efficient to set the values like this for all 100 options. Is there a way to set the values depending on the option selected without having to do it manually?
Below is my code in my ui function
ui <- fluidPage(
titlePanel(h1("Fantasy Dashboard")),
sidebarLayout(
sidebarPanel(h2("Player Name Goes Here"),
selectInput("playername",
label = "Choose a player",
choices = player_choices,
selected = NULL),
),
mainPanel(plotOutput("graph"))
)
)
The choices will be stored in player_choices. These choices are read from a txt file. And depending on the option selected, the variable player should be set to the corresponding value. Thanks in advance!
Try:
library(shiny)
playernames <- list("Smith","Johnston","Andrew")
shinyApp(
ui = fluidPage(
uiOutput("selectname"),
textOutput("result")
),
server = function(input, output) {
output$selectname <- renderUI( {
selectInput("playername", "Choose player",playernames)})
output$result <- renderText({
paste("You chose", input$playername)
})
}
)
The playernames list can also be reactive and be modified by other inputs.

How to restart an lapply loop within a renderUI

I am trying to create a shiny code that is able to filter a table non pre-determined number of times. When the user uploads a different (new) table, unfortunately the code breaks as I need to restart a lapply loop somehow, throwing out the previously stored column names.
I would like to create an non pre-defined filtering options for a table within Shiny. The user can select a column and filter a table choosing different categorical variables within that column. It is possible to add additional selection fields by pressing the 'Add' button.
the UI:
library(shiny)
library(shinydashboard)
library(dplyr)
ui <- shinyUI(
pageWithSidebar(
headerPanel("testing of dynamic number of selection"),
sidebarPanel(
uiOutput("buttons")),
mainPanel(
uiOutput("drops")
,tableOutput("table")
)
))
The server:
A table (test.csv) is automatically stored in a reactive values and a first searching field appears with 3 buttons (Add = to add a new searching field by reading in the colnames and a multiselect that stores the unique variables from that columns. The filtering function is activated by the Calculate button)
server<-function(input, output, session) {
###### read in test file
values<-reactiveValues(number = 1,
upload = NULL,
input = NULL)
values$upload<-read.csv("test.csv")
#just the "add" button, in this instance it shouldn't be a uiOutput
output$buttons <- renderUI({
div(
actionButton(inputId = "add", label = "Add"), actionButton(inputId = "calc", label = "Calculate"),
actionButton(inputId = "new", label = "new table")
)
})
#pressing the add button
observeEvent(input$add, {
cat("i adding a new record\n")
values$number <- values$number + 1L })
daStuff <- function(i){
inputName<-paste0("drop", i)
inputName2<-paste0("select", i)
inputText<-if(values$number>0){input[[paste0("drop",i)]]}else{F} # previously selected value for dropdown
inputSelect <- if(values$number>1){input[[paste0("select",i)]]}else{F} # previously selected value for dropdown
fluidRow(
column(6,selectInput(inputName, inputName, c(colnames(values$upload)), selected = inputText)),
column(6,selectInput(inputName2, inputName2,
na.omit(unique(as.vector(values$upload[,input[[paste0("drop",i)]]]))),
multiple=TRUE, selectize=TRUE, selected=inputSelect)) )}
output$drops<- renderUI({
lapply(seq_len(values$number), daStuff)})
By pressing the Calculate button, the uploaded table is subjected to filtering, depending on the selected unique values and shown in the output$table
observeEvent(input$calc, {
values$input<-NULL
for (i in 1:values$number){
if(!is.null(input[[paste0("select",i)]])){
if(is.null(values$input)){
values$input<- filter(values$upload,values$upload[,input[[paste0("drop",i)]]] %in% input[[paste0("select",i)]])}
else{
values$input<- filter(values$input,values$input[,input[[paste0("drop",i)]]] %in% input[[paste0("select",i)]])}
} }
if (is.null(values$input)){values$input<-values$upload}
output$table <- renderTable({values$input})
})
My problem is when I upload a new table (test2.csv), I don't know how to erase the previously stored selections (drop* and select* values) and gives back an error message.
observeEvent(input$new,{
values$upload<-read.csv("test2.csv")
})
}
shinyApp(ui=ui, server = server)
I suppose I should stop somehow the lapply loop and restart it over, so the previously stored values are replaced depending on the new selection, but I am a bit stuck on how I could achieve that.
Just in case you might still be looking for solutions, I wanted to share something that was similar and could potentially be adapted for your needs.
This uses observeEvent for all select inputs. If it detects any changes, it will update all inputs, including the possibilities for select based on drop.
In addition, when a new file is read, the selectInput for drop and select are reset to first value.
Edit: I forgot to keep selected = input[[paste0("drop",i)]] in place for the dropdown (see revised code). It seems to keep the values now when new filters are added - let me know if this is what you had in mind.
library(shiny)
library(shinydashboard)
library(dplyr)
myDataFrame <- read.csv("test.csv")
ui <- shinyUI(
pageWithSidebar(
headerPanel("Testing of dynamic number of selection"),
sidebarPanel(
fileInput("file1", "Choose file to upload", accept = ".csv"),
uiOutput("buttons")
),
mainPanel(
uiOutput("inputs"),
tableOutput("table")
)
)
)
server <- function(input, output, session) {
myInputs <- reactiveValues(rendered = c(1))
myData <- reactive({
inFile <- input$file1
if (is.null(inFile)) {
d <- myDataFrame
} else {
d <- read.csv(inFile$datapath)
}
d
})
observeEvent(lapply(paste0("drop", myInputs$rendered), function(x) input[[x]]), {
for (i in myInputs$rendered) {
updateSelectInput(session,
paste0('select', i),
choices = myData()[input[[paste0('drop', i)]]],
selected = input[[paste0("select",i)]])
}
})
output$buttons <- renderUI({
div(
actionButton(inputId = "add", label = "Add"),
actionButton(inputId = "calc", label = "Calculate")
)
})
observeEvent(input$add, {
myInputs$rendered <- c(myInputs$rendered, max(myInputs$rendered)+1)
})
observeEvent(input$calc, {
showData <- NULL
for (i in 1:length(myInputs$rendered)) {
if(!is.null(input[[paste0("select",i)]])) {
if(is.null(showData)) {
showData <- filter(myData(), myData()[,input[[paste0("drop",i)]]] %in% input[[paste0("select",i)]])
}
else {
showData <- filter(showData, showData[,input[[paste0("drop",i)]]] %in% input[[paste0("select",i)]])
}
}
}
if (is.null(showData)) { showData <- myData() }
output$table <- renderTable({showData})
})
observe({
output$inputs <- renderUI({
rows <- lapply(myInputs$rendered, function(i){
fluidRow(
column(6, selectInput(paste0('drop',i),
label = "",
choices = colnames(myData()),
selected = input[[paste0("drop",i)]])),
column(6, selectInput(paste0('select',i),
label = "",
choices = myData()[1],
multiple = TRUE,
selectize = TRUE))
)
})
do.call(shiny::tagList, rows)
})
})
}
shinyApp(ui, server)

RStudio and Shiny: define a reactiveValues with a Reactive

I am having a dataframe that is build depending on user input, choosing different filters. I then want to create a bar plot from this custom dataframe where the user can click on to exclude bars from the plot. I basically followed this example:
https://gallery.shinyapps.io/106-plot-interaction-exclude/
However, when I try to define my reactiveValues value with my reactive, I can not define it and get an error. I am suspecting I can not define a reactiveValues with a reactive, is this right? How should I handle this then? Should I use reactives instead of reactiveValues?
Example code:
Server
server <- function(input, output) {
df <- reactive({
input$input1
})
vals2 <- reactive({
(df())
})
output$Id1 <- renderText({
vals2()
})
vals <- reactiveValues()
vals$bla <- df()
}
UI
library(shiny)
ui <- fluidPage(
fluidRow(
column(width= 4,
textInput(inputId = "input1", label = "Select number of rows", value = "10")
),
column(width = 12,
verbatimTextOutput(outputId = "Id1"),
verbatimTextOutput(outputId = "Id2")
)
)
)
Create your reactiveValues towards the beginning of your function and initialise is with a NULL
vals <- reactiveValues(bla = NULL)
You can then write to vals$bla from inside observeEvent, for instance when a button is pressed.
You can read from the reactiveValue, for instance to draw a plot in the form:
output$myPlot <- renderPlot( some function of values$bla )
EDIT updating to add my comment, create an observeEvent which watches your input1, when this changes it will execture the code within {} which will write to your reactiveValue.
observeEvent(input$input1, {
vals$bla <- input$input1
})

Resources