Shiny reactive dataset within observeEvent - r

I have a very simple app which fails. The reason it fails is that the reactive dataset is available solely within the observeEvent function but not outside. I use observeEvent to get datasets from two different sources wrangled. For this example I simply used cbind. My actual code is much more complicated.
This is a logical / syntax related problem but all my searching came up short. In essence I want merged_data() to be available for all parts of the app.
Minimum repr example - this fails because merged_data() is not available outside of the ObserveEvent.
library(shiny)
library(shinyjs)
library(DT)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("testing 1 2 3"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
),
# Show a plot of the generated distribution
mainPanel(
fluidRow(
column(width = 2,
offset = 0,
align = "center",
actionButton(inputId = "fetch_data_inputId",
label = "data")
) #column
,
column(width = 10,
offset = 0,
align = "center",
DT::dataTableOutput("DT1")
) #column
)#fluidrow
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output,session) {
observeEvent(input$fetch_data_inputId, {
req(iris)
button_data <- colnames(iris)
merged_data <- reactive({
if( !is.null(cbind(iris[,1:4],iris3))) {
cbind(iris[,1:4],iris3)
} else {NULL}
})
}) #observeevent
output$DT1 <- renderDataTable({#
rendered_table <- merged_data()
DT::datatable(rendered_table)
})
}
# Run the application
shinyApp(ui = ui, server = server)
Minimum repr example - this works because the datatable is created within the ObserveEvent.
library(shiny)
library(shinyjs)
library(DT)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("testing 1 2 3"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
),
# Show a plot of the generated distribution
mainPanel(
fluidRow(
column(width = 2,
offset = 0,
align = "center",
actionButton(inputId = "fetch_data_inputId",
label = "data")
) #column
,
column(width = 10,
offset = 0,
align = "center",
DT::dataTableOutput("DT1")
) #column
)#fluidrow
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output,session) {
observeEvent(input$fetch_data_inputId, {
req(iris)
button_data <- colnames(iris)
merged_data <- reactive({
if( !is.null(cbind(iris[,1:4],iris3))) {
cbind(iris[,1:4],iris3)
} else {NULL}
})
output$DT1 <- renderDataTable({#
rendered_table <- merged_data()
DT::datatable(rendered_table)
})
}) #observeevent
}
# Run the application
shinyApp(ui = ui, server = server)
What I really need is for the reactive dataset to continue being created within observeEvent but to be accessible outside of the ObserveEvent environment so that i use it in other parts of the app, but I suspect it's the wrong approach. So anything that works would be great.

library(shiny)
library(shinyjs)
library(DT)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("testing 1 2 3"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
),
# Show a plot of the generated distribution
mainPanel(
fluidRow(
column(width = 2,
offset = 0,
align = "center",
actionButton(inputId = "fetch_data_inputId",
label = "data")
) #column
,
column(width = 10,
offset = 0,
align = "center",
DT::dataTableOutput("DT1")
) #column
)#fluidrow
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output,session) {
merged_data <- eventReactive(input$fetch_data_inputId, {
req(iris)
button_data <- colnames(iris)
if( !is.null(cbind(iris[,1:4],iris3))) {
cbind(iris[,1:4],iris3)
} else {NULL}
}) #eventReactive
output$DT1 <- renderDataTable({#
rendered_table <- merged_data()
DT::datatable(rendered_table)
})
}
# Run the application
shinyApp(ui = ui, server = server)

Related

How to add a button to an item in a to do list in shiny

I am using shinydashboardplus.
I'd like to use the to do list but the example in the gallery is limited to just showing a list without any functionality.
To track a todo list for a user I am reading and writing to a csv for the moment.
I can read the csv to dynamically populate the list. Now I'd like to be able to strike through an item to indicate it is completed using the checked parameter.
The checked items should be removed from the csv.
Ill work on the adding items another day I think....
Here is my example (not reading from csv but from iris for this example).
library(shiny)
library(shinydashboardPlus)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Old Faithful Geyser Data"),
useShinydashboardPlus(),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
),
# Show a plot of the generated distribution
mainPanel(
box(
"Sortable todo list demo",
status = "warning",
todoList(
apply(mtcars,1, function(x)
todoListItem(
label = x[1],
x[2]
)
)
)
)
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
}
# Run the application
shinyApp(ui = ui, server = server)
Here is an approach using renderUI and a reactive data.frame:
library(shiny)
library(shinydashboardPlus)
library(shinyWidgets)
css <- "
.inlinecheckbox .shiny-input-container {
display: inline-block;
width: auto;
}
"
ui <- fluidPage(
tags$style(css),
titlePanel("Dynamic to do list"),
useShinydashboardPlus(),
sidebarLayout(
sidebarPanel(),
mainPanel(
box(
"Sortable todo list demo",
status = "warning",
uiOutput("myToDoList")
)
)
)
)
checkboxIDs <- paste0("checkbox", seq_len(nrow(mtcars)))
mtcars$checked <- FALSE
# Define server logic required to draw a histogram
server <- function(input, output) {
reactiveMtcars <- reactiveVal(mtcars)
observe({
for (i in seq_along(checkboxIDs)) {
if(!is.null(input[[checkboxIDs[1]]])){
mtcars$checked[i] <- input[[checkboxIDs[i]]]
}
}
reactiveMtcars(mtcars)
})
output$myToDoList <- renderUI({
req(reactiveMtcars())
todoListItems <- list()
for(i in seq_len(nrow(reactiveMtcars()))){
todoListItems[[i]] <- todoListItem(
label = div(rownames(reactiveMtcars())[i], style = ""),
span(class = "inlinecheckbox", checkboxInput(inputId = paste0("checkbox", i), label = NULL, value = reactiveMtcars()$checked[i])),
checked = reactiveMtcars()$checked[i],
)
}
todoList(todoListItems)
})
}
shinyApp(ui = ui, server = server)

Hide plot when action button or slider changes in R Shiny

I have a small Shiny app that generates some data whenever the New data button is pressed. The Show plot button shows a hidden plot. I would like the plot to be hidden again automatically whenever the New data button is pressed to make a new data set. A bonus would be for the plot to be hidden also as soon as the slider is changed. I am not looking for a toggle action.
I tried adapting this example that uses conditional panel but I could not successfully figure out how to correctly change the values$show between TRUE and FALSE.
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput(inputId = "number",
label = "Pick a number",
min = 6,
max = 12,
value = 8),
actionButton("new_data",
"New data"),
actionButton("show_plot",
"Show plot")
),
mainPanel(
tableOutput("char_table"),
plotOutput(outputId = "car_plot")
)
)
)
server <- function(input, output) {
t <- eventReactive(input$new_data, {
r <- input$number
c <- r - 1
mat <- matrix(sample(0:1,r*c, replace=TRUE),r,c)
})
output$char_table <- renderTable({
t()
})
p <- eventReactive(input$show_plot, {
plot(cars)
})
output$car_plot <- renderPlot({
p()
})
}
shinyApp(ui = ui, server = server)
You can use a reactive value and a if to control the plot.
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput(inputId = "number",
label = "Pick a number",
min = 6,
max = 12,
value = 8),
actionButton("new_data",
"New data"),
actionButton("show_plot",
"Show plot")
),
mainPanel(
tableOutput("char_table"),
plotOutput(outputId = "car_plot")
)
)
)
server <- function(input, output) {
showPlot <- reactiveVal(FALSE)
t <- eventReactive(input$new_data, {
showPlot(FALSE)
r <- input$number
c <- r - 1
mat <- matrix(sample(0:1,r*c, replace=TRUE),r,c)
})
output$char_table <- renderTable({
t()
})
observeEvent(input$number, {
showPlot(FALSE)
})
observeEvent(input$show_plot, {
showPlot(TRUE)
})
output$car_plot <- renderPlot({
if (showPlot())
plot(cars)
})
}
shinyApp(ui = ui, server = server)
Alternate solution using shinyjs which is handy in these situations.
library(shiny)
library(shinyjs)
ui <- fluidPage( shinyjs::useShinyjs(),
sidebarLayout(
sidebarPanel(
sliderInput(inputId = "number",
label = "Pick a number",
min = 6,
max = 12,
value = 8),
actionButton("new_data",
"New data"),
actionButton("show_plot",
"Show plot")
),
mainPanel(
tableOutput("char_table"),
plotOutput(outputId = "car_plot")
)
)
)
server <- function(input, output) {
t <- eventReactive(input$new_data, {
hide("car_plot")
r <- input$number
c <- r - 1
mat <- matrix(sample(0:1,r*c, replace=TRUE),r,c)
})
output$char_table <- renderTable({
t()
})
observeEvent(input$show_plot, {
show("car_plot")
})
output$car_plot <- renderPlot({
plot(cars)
})
}
shinyApp(ui = ui, server = server)

Using Conditionalpanel Function in Shiny

I'm trying to create the scenario whereby using conditionalpanel, I am able to have an user input of checked boxes to display either 1 or 2 plots, one after another.
My reproducible code can be found below, however, I am unable to display the plots.
Could someone please share with me where did I make a mistake?
library(shiny)
ui = fluidPage(
titlePanel("Plot1 or Plot2?"),
sidebarLayout(
sidebarPanel(
checkboxGroupInput("my_choices", "Plot1 or Plot2",choices = c("Plot1", "Plot2"), selected = "Plot1"),width=2),
mainPanel(
conditionalPanel(
condition = "input.my_choices == 'Plot1'",
plotOutput("plot1")
),
conditionalPanel(
condition = "input.my_choices == 'Plot2'",
plotOutput("plot2")
),
conditionalPanel(
condition = "input.my_choices.includes('Plot1', 'Plot2')",
plotOutput("plot1"),
plotOutput("plot2")
)
)
)
)
server = function(input, output) {
output$plot1 <- renderPlot({plot(iris)})
output$plot2 <- renderPlot({plot(mtcars)})
}
shinyApp(ui, server)
Update:
I've got what I wanted but without using ConditionalPanel function. Here's the code below:
Would appreciate if someone can share with me the proper way of using ConditionalPanel Function! (:
library(shiny)
#data
df <- iris
#ui
ui <- fluidPage(
sidebarPanel(
checkboxGroupInput(inputId = "Question",
label = "Choose the plots",
choices = c("Plot1", "Plot2", "Plot3"),
selected = "")),
mainPanel(
uiOutput('ui_plot')
)
)
#server
server <- function(input, output)
{
# gen plot containers
output$ui_plot <- renderUI({
out <- list()
if (length(input$Question)==0){return(NULL)}
for (i in 1:length(input$Question)){
out[[i]] <- plotOutput(outputId = paste0("plot",i))
}
return(out)
})
# render plots
observe({
for (i in 1:3){
local({ #because expressions are evaluated at app init
ii <- i
output[[paste0('plot',ii)]] <- renderPlot({
if ( length(input$Question) > ii-1 ){
return(plot(runif(100)))
}
NULL
})
})
}
})
}
shinyApp(ui, server)
I would give you an alternative as you will need to create new plots with different id in order for that to work. The simplest one I can think of is using shinyjs package and its hide and show functions. You can also do this via renderUI but you shouldn't give unnecessary work to your server only if you're showing and hiding the elements
library(shiny)
library(shinyjs)
ui = fluidPage(
useShinyjs(),
titlePanel("Plot1 or Plot2?"),
sidebarLayout(
sidebarPanel(
checkboxGroupInput("my_choices", "Plot1 or Plot2",choices = c("Plot1", "Plot2"), selected = "Plot1"),width=2),
mainPanel(
plotOutput("plot1"),
plotOutput("plot2")
)
)
)
server = function(input, output,session) {
# hide plots on start
hide("plot1");hide("plot2")
output$plot1 <- renderPlot({plot(iris)})
output$plot2 <- renderPlot({plot(mtcars)})
observeEvent(input$my_choices,{
if(is.null(input$my_choices)){
hide("plot1"); hide("plot2")
}
else if(length(input$my_choices) == 1){
if(input$my_choices == "Plot1"){
show("plot1");hide("plot2")
}
if(input$my_choices == "Plot2"){
hide("plot1");show("plot2")
}
}
else{
if(all(c("Plot1","Plot2") %in% input$my_choices)){
show("plot1");show("plot2")
}
}
},ignoreNULL = F)
}
shinyApp(ui, server)

How to create a UI in Shiny from a for loop whose length is based on numeric input?

As an extension of this example:
https://shiny.rstudio.com/gallery/creating-a-ui-from-a-loop.html
Say you would like the for loop to be of a length determined by a numeric input. So for example, extending the linked example (using just the second part of it):
ui <- fluidPage(
title = 'Creating a UI from a dynamic loop length',
sidebarLayout(
sidebarPanel(
# Determine Length of Loop
numericInput(inputId = "NumLoop", "Number of Loops", value = 5, min = 1, max = 5, step = 1)
),
mainPanel(
# UI output
lapply(1:input.NumLoop, function(i) {
uiOutput(paste0('b', i))
})
)
)
)
server <- function(input, output, session) {
reactive({
lapply(1:input$NumLoop, function(i) {
output[[paste0('b', i)]] <- renderUI({
strong(paste0('Hi, this is output B#', i))
})
})
})
}
shinyApp(ui = ui, server = server)
As far as I can tell there are two problems with the code:
In the UI, I don't know how to legitimately use the input from NumLoop in the for loop of the UI output. I have experimented with the conditionalPanel function with no luck.
In the server, once I put the loop behind a reactive function to make use of input$NumLoop I no longer have access to those renderUI outputs in the UI.
Any ideas of how to solves these issues would be much appreciated.
This should do the trick, as per #Dean, yes the second renderUI shouldn't be there
library(shiny)
ui <- fluidPage(
title = 'Creating a UI from a dynamic loop length',
sidebarLayout(
sidebarPanel(
# Determine Length of Loop
numericInput(inputId = "NumLoop", "Number of Loops", value = 5, min = 1, max = 10, step = 1)
),
mainPanel(
# UI output
uiOutput('moreControls')
)
)
)
server <- function(input, output, session) {
output$moreControls <- renderUI({
lapply(1:input$NumLoop, function(i) {
strong(paste0('Hi, this is output B#', i),br())
})
})
}
shinyApp(ui = ui, server = server)

Disasble shiny sliderInput using shinyjs

I am building multiple lm() models using dplyr. I want to allow a user to change the independent variable value in a Shiny app - via shiny::sliderInput(). But only do so where "goodness of fit" say R^2 is greater than a threshold - otherwise disable the slider. I have tried to use the shinyjs::disable() function. See below, but can't get it to work. Any ideas on what I am doing wrong ?
library(shiny)
library(shinyjs)
# Define UI for application that draws a histogram
ui <- shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("test","Nice number",min = 1,max = 50,value = 30)
),
mainPanel(
textOutput("valueText")
)
)
))
# Define server to disable slider if value selected
server <- shinyServer(function(input, output) {
value <- reactive(input$test)
output$valueText <- renderText(paste(value()))
#How to diasble slider?
reactive(if(value()==35){
shinyjs::disable('test')
}
)
})
# Run the application
shinyApp(ui = ui, server = server)
You have to call useShinyjs() in ui.R.
This is the code:
library(shiny)
library(shinyjs)
# Define UI for application that draws a histogram
ui <- shinyUI(
tagList(
useShinyjs(),
fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("test","Nice number",min = 1,max = 50,value = 30)
),
mainPanel(
textOutput("valueText")
)
)
)
)
)
# Define server to disable slider if value selected
server <- shinyServer(function(input, output) {
value <- reactive(input$test)
output$valueText <- renderText(paste(value()))
#How to diasble slider?
observeEvent(value(), {
if(value()==35){
shinyjs::disable('test')
}
})
})
# Run the application
shinyApp(ui = ui, server = server)

Resources