Warning: Error in [[<-.data.frame: replacement has 1 row, data has 0 - r

My objective is to plot some graphs based on the data uploaded by users. Users can select how it is plotted. It works but there are warnings showing in the console. I think the issue lies with how the server module looks up values in the UI module but the UI module is not yet initalised. How can I get rid of the warnings? Thanks
Sample data:
fwrite(data.table(
age.band = c("45-54","55-64","55-64","55-64","55-64","45-54","35-44","25-34"),
gender = c("MALE","FEMALE","FEMALE","FEMALE","FEMALE","FEMALE","MALE","FEMALE"),
event = c("13/04/2022","8/04/2022","20/05/2021","12/02/2022","19/02/2021","19/03/2022","16/03/2021","19/03/2021"),
cause = c('Cancer','Cancer','Cancer','Mental Illness','Cancer','Musculoskeletal','Mental Illness','Musculoskeletal'),
type= c('Type1','Type1','Type1','Type2','Type2','Type2','Type2','Type2'),
rate = rep(1,8),
status = rep("accepted",8)
),"sample.csv",row.names=F)
Here is my code:
### UI and server to load data
loadDataUI <- function(id,label = "Upload",buttonLabel = "Browse") {
ns <- NS(id)
tagList(
fileInput(ns("file"),label=label,buttonLabel=buttonLabel,accept = c('.csv'),placeholder = "No file selected")
)
}
loadData <- function(id) {
moduleServer(
id,
function(input, output, session) {
userFile <- reactive({
shiny::validate(need(input$file, message = FALSE))
input$file
})
dt <- reactive({fread(userFile()$datapath)})
return(dt)
}
)
}
### UI and server to plot data
edaUI <- function(id,cat.option) {
ns <- NS(id)
tagList(
box(
dropdown(
selectInput(inputId = ns('category'),
label = 'Category',
choices = cat.option,selected = "all"),
dateRangeInput(ns("eventdaterange"), "Loss date range",
start = Sys.Date()-10,
end = Sys.Date()+10)
),
highchartOutput(ns("hc_init"))
)
)
}
eda <- function(id,filtered.data,measure){
moduleServer(
id,
function(input, output, session) {
observeEvent(filtered.data(), {
updateDateRangeInput(session,"eventdaterange",
start = min(filtered.data()$event),
end = max(filtered.data()$event))
})
output$hc_init <- renderHighchart({
tmp <- filtered.data() %>%
filter(event >= input$eventdaterange[1],event <= input$eventdaterange[2]) %>%
mutate(event.date = as.character(as.yearqtr(event)))
if (input$category == "all") {
tmp %>% group_by(event.date) %>%
summarise(measure=sum(!!sym(measure))) %>%
hchart("line", hcaes(x = event.date, y = measure)) %>%
hc_title(text="Chart")
} else {
tmp %>%
group_by(cat = !!sym(input$category),event.date) %>%
summarise(measure=sum(!!sym(measure))) %>% ungroup() %>%
arrange(cat,event.date) %>%
hchart("line", hcaes(x = event.date, y = measure,group = cat)) %>%
hc_title(text="Chart")
}
})
}
)
}
### Main UI and server
ui <- function() {
fluidPage(useShinydashboard(),
navbarPage(
tabPanel(
title = "Data analysis",
sidebarLayout(
sidebarPanel(
width = 2,
loadDataUI("input","","Upload data"),
awesomeRadio(
inputId = "benefit",
label = strong("Select type"),
choices = c("Type1", "Type2"),
selected = NULL,inline = TRUE,checkbox = FALSE)
),
mainPanel(
width = 10,edaUI("dt1",cat.option=c("all","age.band","gender"))
)
)
)
)
)
}
server <- function(input, output, session) {
data <- loadData("input")
filtered_data <- reactive({
date.cols <- c("event")
data()[type == input$benefit][,(date.cols):= lapply(.SD, dmy),.SDcols = date.cols]
})
eda("dt1",filtered_data,measure="rate")
}
shinyApp(ui = ui, server = server)

Related

Shiny bookmark - unable to restore some of user's selections

I have just started include bookmark functionality to my app. My app have a few selection inputs which do not seem to be restored and I couldnt figure out the problems.
The app has 2 key inputs:
The radio button on the sidebar
The date/number ranges
At the current state, the app cant seem to restore the radio button (if I switch to duration, it just doesnt work)
The simplify code is below:
library(shiny)
library(data.table)
library(tidyverse)
library(lubridate)
library(shinydashboard)
library(zoo)
library(shinyWidgets)
library(nycflights13)
flight.dt <- flights %>% mutate(flight.date =ymd(substr(time_hour,1,10)),duration=round(air_time,-2))
### --------Analyse module ---------------------------
plotUI <- function(id) {
ns <- NS(id)
tagList(
uiOutput(ns("groupmenu")),
plotOutput(ns("plot"))
)
}
plotServer <- function(id,method,carr) {
moduleServer(
id,
function(input, output, session) {
filtered.data <- reactive(flight.dt %>% filter(carrier == carr))
output$groupmenu <- renderUI({
getselection <- if (method() != "duration") c("Year Quarter"="yearqtr") else c("Duration"="dur.grp")
rng.min <- switch (method(),
"date" = min(filtered.data()$flight.date),
"duration" = max(0,min(filtered.data()$duration,na.rm = TRUE)))
rng.max <- switch(method(),
"date" = max(filtered.data()$flight.date),
"duration" = max(0,max(filtered.data()$duration,na.rm = TRUE)))
ns <- session$ns
tagList(
fluidRow(
column(2,selectInput(ns("group"), "Group by:",choices =as.list(getselection))),
conditionalPanel("input.method == 'date'",
dateRangeInput(ns("daterange"),"Date range:",start = rng.min,end = rng.max,format = "dd/mm/yyyy", separator = " - ")),
conditionalPanel("input.method == 'duration'",
numericRangeInput(ns("durrange"), label = "Duration range:",value = c(rng.min,rng.max)))
)
)
})
dt <- reactiveVal(NULL)
observeEvent(input$group,{
tmp <- filtered.data() %>% mutate(sel.method = switch(method(),"date" = flight.date,"duration" = duration))
if (input$group == "yearqtr") {
tmp$key <- paste0(year(tmp$sel.method),"-Q",quarter(tmp$sel.method))
} else if (input$group == "dur.grp") {
tmp$key <- tmp$duration
}
dt(tmp)
})
dt.sum <- reactive({
req(dt())
setDT(dt())
if (method() == "date") {
tmp <- dt()[sel.method >= input$daterange[1] & sel.method <= input$daterange[2]]
} else if (method() == "duration") {
tmp <- dt()[key >= input$durrange[1] & key <= input$durrange[2]]
}
tmp %>% group_by(key) %>% dplyr::summarise(count=n())
})
output$plot <- renderPlot({
dt.sum() %>% ggplot(aes(x = as.character(key), y = count)) + geom_col()
})
}
)
}
### UI part -----------------------
header <- dashboardHeader(title = 'Analysis')
sidebar <- dashboardSidebar(
radioButtons("method", "Select method:",c("Date" = "date","Duration" = "duration"),selected = "date"),
bookmarkButton()
)
body <- dashboardBody(uiOutput('tablist'))
ui <- function(request) {
dashboardPage(title="Analysis", header,sidebar,body)
}
### Server part ----------------------------
server = function(input, output) {
method <- reactive(input$method)
carr <- reactive(unique(flight.dt$carrier)[1:3])
ntabs <- reactive(length(carr()))
observeEvent(ntabs(),{
lapply(1:ntabs(), function (i) plotServer(paste0("count",i),method,carr()[i]))
})
output$tablist = renderUI({
addtabs <- lapply(1:ntabs(),function (i) {
tabPanel(carr()[i],plotUI(paste0("count",i)))
})
do.call(tabsetPanel, addtabs)
})
}
### Running part ----------------------------
enableBookmarking("server")
shinyApp(ui, server)
Put your ui elements you want restored within ui function. See simplified example:
library(shiny)
library(shinydashboard)
### UI part -----------------------
ui <- function(request) {
header <- dashboardHeader(title = 'Analysis')
sidebar <- dashboardSidebar(
radioButtons("method", "Select method:",c("Date" = "date","Duration" = "duration"),selected = "date"),
bookmarkButton()
)
body <- dashboardBody("BODY")
dashboardPage(title="Analysis", header,sidebar,body)
}
### Server part ----------------------------
server = function(input, output) {
}
### Running part ----------------------------
enableBookmarking("server")
shinyApp(ui, server)

Update data SelectInput in shiny modules

I am facing an issue in updating the data selected using SelectInput and modules in Shiny. In a few words, when I select the data to be loaded into the selectInput panel, it updates it on the first selection, but if I then want to go from dataset 1 to dataset 2, the data does not update.
Below you cand find the code to reproduce the specific problem.
# Libraries
pacman::p_load(shiny, shinydashboard,
tidyverse, data.table, DT, stringr,
ggplot2, plotly,
survival, survminer, GGally, scales,
shinycssloaders)
version <- 0.1
# GENERAL PARAMETERS
box.height <<- 700
select.box.height <<- 150
selectAB.box.height <<- 250
select.box.width <<- 12
# Data
men1_1.norm <<- as.numeric(rnorm(50))
men1_1.pois <<- as.numeric(rpois(50, lambda = 1))
men1_2.norm <<- as.numeric(rnorm(50, mean = 1))
men1_2.pois <<- as.numeric(rpois(50, lambda = 2))
# ui modules
LoadDataUI <- function(id,
label = "Select the data:",
sel = "Data 1",
choic = c('Data 1','Data 2')){
ns <- NS(id)
selectInput(ns("data.sel"),
label = label,
choices = choic,
selected = sel)
}
PlotUI <- function(id){
ns <- NS(id)
plotOutput(ns("plot"))
}
# ui
ui <- dashboardPage(
dashboardHeader(title = paste('My Dashboard',version,sep='')),
dashboardSidebar(
sidebarMenu(
id = "sbMenu",
#Tabs for different data displays
menuItem("1st Menu", tabName = "men1", icon = icon('microscope'))
)
),
dashboardBody(
tabItems(
tabItem(tabName = 'men1',
h2(strong('tab 1')),
fluidRow(
### !!!! TO REMOVE ERROR MESSAGES !!!!
# tags$style(type="text/css",
# ".shiny-output-error { visibility: hidden; }",
# ".shiny-output-error:before { visibility: hidden; }"
#,
box(title='Select data to load:', height= select.box.height, width = select.box.width,
LoadDataUI("data1")
),
box(title='Normal', height=box.height,
PlotUI("hist_norm1")
),
box(title='Poisson', height=box.height,
PlotUI("hist_pois1")
)
)
)
)
)
)
# server modules
Panel <- function(id){
moduleServer(
id,
function(input, output, session) {
return(
list(
data = reactive({input$data.sel})
)
)
}
)
}
LoadDataServer <- function(id, menu, data_selected
){
moduleServer(
id,
function(input, output, session){
dt <- reactive(data_selected)
data <- reactiveValues(norm = NULL,
pois = NULL)
data$norm <- reactive({get(paste0(menu(),"_", dt(), ".norm"), envir = .GlobalEnv)})
data$pois <- reactive({get(paste0(menu(),"_", dt(), ".pois"), envir = .GlobalEnv)})
return(
data
)
}
)
}
PlotServer <- function(id,data = NULL){
moduleServer(
id,
function(input, output, session) {
x <- reactive(as.numeric(data))
output$plot <- renderPlot({
hist(x(), col = 'darkgray', border = 'white')
})
# output$plot <- renderPlot({
# if(is.null(data)){return(NULL)}else{
# hist(data, col = 'darkgray', border = 'white')}
# })
}
)
}
# server
server <- function(input, output, session){
data1 <- Panel("data1")
observeEvent(data1$data(), {
updateSelectInput(session, 'data.sel', selected = input$data.sel)
})
pnl1 <- reactive(
switch(data1$data(),
"Data 1" = "1",
"Data 2" = "2")
)
d1 <- LoadDataServer("data1", menu = reactive({input$sbMenu}), data_selected = pnl1())
# Plot
# menu1
output$plot <- PlotServer("hist_norm1", data = d1$norm())
output$plot <- PlotServer("hist_pois1", data = d1$pois())
}
shinyApp(ui, server)
Thanks!
Try this
version <- 0.1
# GENERAL PARAMETERS
box.height <<- 500
select.box.height <<- 150
selectAB.box.height <<- 250
select.box.width <<- 12
# Data
men1_1.norm <<- as.numeric(rnorm(50))
men1_1.pois <<- as.numeric(rpois(50, lambda = 1))
men1_2.norm <<- as.numeric(rnorm(150, mean = 1))
men1_2.pois <<- as.numeric(rpois(150, lambda = 2))
# ui modules
LoadDataUI <- function(id,
label = "Select the data:",
sel = "Data 1",
choic = c('Data 1','Data 2')){
ns <- NS(id)
selectInput(ns("data.sel"),
label = label,
choices = choic,
selected = sel)
}
PlotUI <- function(id){
ns <- NS(id)
tagList(
plotOutput(ns("plot"))
)
}
# ui
ui <- dashboardPage(
dashboardHeader(title = paste('My Dashboard',version,sep='')),
dashboardSidebar(
sidebarMenu(
id = "sbMenu",
#Tabs for different data displays
menuItem("1st Menu", tabName = "men1", icon = icon('microscope'))
)
),
dashboardBody(
tabItems(
tabItem(tabName = 'men1',
h2(strong('tab 1')),
fluidRow(
### !!!! TO REMOVE ERROR MESSAGES !!!!
# tags$style(type="text/css",
# ".shiny-output-error { visibility: hidden; }",
# ".shiny-output-error:before { visibility: hidden; }"
#,
box(title='Select data to load:', height= select.box.height, width = select.box.width,
LoadDataUI("data1")
),
box(title='Normal', height=box.height,
PlotUI("hist_norm1")
),
box(title='Poisson', height=box.height,
PlotUI("hist_pois1")
)
)
)
)
)
)
# server modules
Panel <- function(id){
moduleServer(
id,
function(input, output, session) {
return(
list(
data = reactive({input$data.sel})
)
)
}
)
}
LoadDataServer <- function(id, menu, data_selected
){
moduleServer(
id,
function(input, output, session){
dt <- reactive(
switch(data_selected(),
"Data 1" = "1",
"Data 2" = "2")
)
observe({print(dt())})
data <- reactiveValues(norm = NULL,
pois = NULL)
data$norm <- reactive({get(paste0(menu(),"_", dt(), ".norm"), envir = .GlobalEnv)})
data$pois <- reactive({get(paste0(menu(),"_", dt(), ".pois"), envir = .GlobalEnv)})
return(
data
)
}
)
}
PlotServer <- function(id,data){
moduleServer(
id,
function(input, output, session) {
#x <- reactive(as.numeric(data))
output$plot <- renderPlot({
x <- as.numeric(data())
hist(x, col = 'darkgray', border = 'white')
})
# output$plot <- renderPlot({
# if(is.null(data)){return(NULL)}else{
# hist(data, col = 'darkgray', border = 'white')}
# })
}
)
}
# server
server <- function(input, output, session){
data1 <- Panel("data1")
# observeEvent(data1$data(), {
# updateSelectInput(session, 'data.sel', selected = input$data.sel)
# })
# pnl1 <- reactive(
# switch(data1$data(),
# "Data 1" = "1",
# "Data 2" = "2")
# )
d1 <- LoadDataServer("data1", menu = reactive({input$sbMenu}), data_selected = data1$data )
# Plot
# menu1
PlotServer("hist_norm1", data = reactive(d1$norm()) )
PlotServer("hist_pois1", data = reactive(d1$pois()) )
}
shinyApp(ui, server)
The problem arises because the data you pass to the PlotServer is not reactive. I've made the additional changes:
stored the data in the beginning in a list to avoid using get; it's easier and safer to directly work with a data object
removed the data_selected argument from the LoadDataServer as this information is determined by the input$data.sel variable, however this is only accessible from within the module and not the the main app server. For the initialisation, you need this information only in the UI part of the module (which you already have implemented). This allows me to remove observeEvent code in your main app server as this is handled by the module.
# Libraries
# pacman::p_load(shiny, shinydashboard,
# tidyverse, data.table, DT, stringr,
# ggplot2, plotly,
# survival, survminer, GGally, scales,
# shinycssloaders)
library(shiny)
library(shinydashboard)
library(ggplot2)
version <- 0.1
# GENERAL PARAMETERS
box.height <<- 700
select.box.height <<- 150
selectAB.box.height <<- 250
select.box.width <<- 12
# Data
data_object <- list(
men1_1 = list(
norm = as.numeric(rnorm(50)),
pois = as.numeric(rpois(50, lambda = 1))
),
men1_2 = list(
norm = as.numeric(rnorm(50, mean = 1)),
pois = as.numeric(rpois(50, lambda = 2))
)
)
# ui modules
LoadDataUI <- function(id,
label = "Select the data:",
sel = "Data 1",
choic = c('Data 1' = "1",'Data 2' = "2")){
ns <- NS(id)
selectInput(ns("data.sel"),
label = label,
choices = choic,
selected = sel)
}
PlotUI <- function(id){
ns <- NS(id)
plotOutput(ns("plot"))
}
# ui
ui <- dashboardPage(
dashboardHeader(title = paste('My Dashboard',version,sep='')),
dashboardSidebar(
sidebarMenu(
id = "sbMenu",
#Tabs for different data displays
menuItem("1st Menu", tabName = "men1", icon = icon('microscope'))
)
),
dashboardBody(
tabItems(
tabItem(tabName = 'men1',
h2(strong('tab 1')),
fluidRow(
### !!!! TO REMOVE ERROR MESSAGES !!!!
# tags$style(type="text/css",
# ".shiny-output-error { visibility: hidden; }",
# ".shiny-output-error:before { visibility: hidden; }"
#,
box(title='Select data to load:', height= select.box.height, width = select.box.width,
LoadDataUI("data1")
),
box(title='Normal', height=box.height,
PlotUI("hist_norm1")
),
box(title='Poisson', height=box.height,
PlotUI("hist_pois1")
)
)
)
)
)
)
# server modules
Panel <- function(id){
moduleServer(
id,
function(input, output, session) {
return(
list(
data = reactive({input$data.sel})
)
)
}
)
}
LoadDataServer <- function(id, menu
){
moduleServer(
id,
function(input, output, session){
data <- reactiveValues(norm = NULL,
pois = NULL)
observeEvent(input$data.sel, {
data$norm <- data_object[[paste0(menu(), "_", input$data.sel)]][["norm"]]
data$pois <- data_object[[paste0(menu(), "_", input$data.sel)]][["pois"]]
})
return(
data
)
}
)
}
PlotServer <- function(id,data = NULL){
moduleServer(
id,
function(input, output, session) {
output$plot <- renderPlot({
hist(data(), col = 'darkgray', border = 'white')
})
}
)
}
# server
server <- function(input, output, session){
d1 <- LoadDataServer("data1", menu = reactive({input$sbMenu}))
# Plot
# menu1
output$plot <- PlotServer("hist_norm1", data = reactive({d1$norm}))
output$plot <- PlotServer("hist_pois1", data = reactive({d1$pois}))
}
shinyApp(ui, server)
If you pass the complete d1 object to the PlotServer, you could remove the reactive({}) you currently need to pass the norm or pois data.
I recommend to read into how to pass data between modules and module capsulation, you can start with mastering shiny or my introduction to modules.

How to force inputs to take on the same values across modules

I'm working on a shiny dashboard that makes heavy use of shiny modules and my client has asked me to make it so that the same two inputs from my dashboard's various tabs take on the same values regardless of tab. I'm having a huge problem doing this and was able to recreate it using a toy example that you'll find below.
#app.R
library(data.table)
library(shiny)
library(ggplot2)
library(ggthemes)
library(shinythemes)
source("Modules.R")
penguins <<- as.data.table(palmerpenguins::penguins)
ui = uiOutput("ui")
inputs <<- reactiveValues(species = NULL, island = NULL)
server <- function(input, output, session) {
bill_species_server("tab1")
flipper_mass_scatter_server("tab2")
output$ui = renderUI({
fluidPage(
titlePanel("", "Penguin Dashboard"),
tabsetPanel(
tabPanel("Bill Length by Species",
ui_code("tab1")
),
tabPanel("Flipper Length by Body Mass",
ui_code("tab2")
)
)
)
})
}
# Run the application
shinyApp(ui = ui, server = server)
#Modules.R
ui_code = function (id) {
ns = NS(id)
sidebarLayout(position = "left",
sidebarPanel(
selectInput(ns("species"), "Choose 1+ species:", choices = penguins[, sort(unique(species))], multiple = TRUE),
selectInput(ns("island"), "Choose 1+ islands:", choices = penguins[, sort(unique(island))], multiple = TRUE)
),
mainPanel(
plotOutput(ns("plot"))
)
)
}
bill_species_server = function(id) {
moduleServer(id, function(input, output, session) {
observeEvent(inputs$species, ignoreInit = TRUE, ignoreNULL = TRUE, {
if (length(inputs$species) > 0) {
updateSelectInput(session = session, inputId = "species", selected = inputs$species)
}
})
observeEvent(inputs$island, ignoreInit = TRUE, ignoreNULL = TRUE, {
if (length(inputs$island) > 0) {
updateSelectInput(session = session, inputId = "island", selected = inputs$island)
}
})
output$plot = renderPlot({
if (length(input$species) > 0) {
penguins = penguins[species %in% input$species]
}
if (length(input$island) > 0) {
penguins = penguins[island %in% input$island]
}
ggplot(penguins) + geom_histogram(aes(x = `bill_length_mm`, fill = species)) + scale_fill_canva(palette = "Striking and energetic")
})
observeEvent(input$species, ignoreNULL = TRUE, ignoreInit = TRUE, {
inputs$species = input$species
})
observeEvent(input$island, ignoreNULL = TRUE, ignoreInit = TRUE, {
inputs$island = input$island
})
})
return(inputs)
}
flipper_mass_scatter_server = function (id) {
moduleServer(id, function(input, output, session) {
observeEvent(inputs$species, ignoreInit = TRUE, ignoreNULL = TRUE, {
if (length(inputs$species) > 0) {
updateSelectInput(session = session, inputId = "species", selected = inputs$species)
}
})
observeEvent(inputs$island, ignoreInit = TRUE, ignoreNULL = TRUE, {
if (length(inputs$island) > 0) {
updateSelectInput(session = session, inputId = "island", selected = inputs$island)
}
})
output$plot = renderPlot({
if (length(input$species) > 0) {
penguins = penguins[species %in% input$species]
}
if (length(input$island) > 0) {
penguins = penguins[island %in% input$island]
}
ggplot(penguins) + geom_point(aes(x = `flipper_length_mm`, y = body_mass_g, colour = species)) + scale_colour_canva(palette = "Striking and energetic")
})
observeEvent(input$species, ignoreNULL = TRUE, ignoreInit = TRUE, {
inputs$species = input$species
})
observeEvent(input$island, ignoreNULL = TRUE, ignoreInit = TRUE, {
inputs$island = input$island
})
})
return(inputs)
}
So the two inputs that I'm trying to link in this toy example are species and island. I've set it up so that when someone makes a new selection on either input, an observer should update a global variable which in this case I've labelled inputs. And then if inputs is updated, the other tab should then update its own selectInput.
Weirdly, I find that with this code, if I make my selections kind of slowly, all works just fine! However, the moment that I click 2+ choices in rapid succession, it causes an infinite loop to happen in the current tab where the second choice appears, then disappears, then appears... etc. Conversely, when I have 3 choices selected and try to delete options in rapid succession, it just doesn't let me delete all choices!!
So weird.
Anyone know what the problem is with my code, and how I can force the inputs in both tabs to keep the same values as chosen in the other tabs?
Thanks!
I significantly restructured how I approached this problem and came up with a solution. Basically, I used shinydashboard and decided that I would define the species and island selectInput controls outside of my modules.
The values to those controls were then passed to the modules as reactive objects that were then used to filter the data before the data got plotted. This works so much better now! Have a look at my two files:
#app.R
library(data.table)
library(shiny)
library(ggplot2)
library(ggthemes)
library(shinythemes)
library(shinydashboard)
source("Modules.R")
penguins <<- as.data.table(palmerpenguins::penguins)
ui = dashboardPage(header = dashboardHeader(title = "Penguin Dashboard"),
sidebar = dashboardSidebar(
sidebarMenu(id = "tabs",
selectInput("species", "Choose 1+ species:", choices = penguins[, sort(unique(species))], multiple = TRUE),
selectInput("island", "Choose 1+ islands:", choices = penguins[, sort(unique(island))], multiple = TRUE),
menuItem("Bill Length by Species", expandedName = "tab1", tabName = "tab1", startExpanded = TRUE,
sliderInput("mass", "Select a range of body masses:",
min = penguins[, min(body_mass_g, na.rm=TRUE)],
max = penguins[, max(body_mass_g, na.rm=TRUE)],
value = penguins[, range(body_mass_g, na.rm=TRUE)])
),
menuItem("Flipper Length by Body Mass", expandedName = "tab2", tabName = "tab2",
checkboxGroupInput("sex", "Choose sex of penguins:",
choices = c("male","female")))
)),
body = dashboardBody(
uiOutput("plots")
)
)
#inputs <<- reactiveValues(species = NULL, island = NULL)
server <- function(input, output, session) {
#inputs <- reactiveValues(species=input$species, island=input$island)
in_species = reactive({input$species})
in_island = reactive({input$island})
in_mass = reactive({input$mass})
in_sex = reactive({input$sex})
bill_species_server("tab1", in_species, in_island, in_mass)
flipper_mass_scatter_server("tab2", in_species, in_island, in_sex)
output$plots = renderUI({
validate(need(!is.null(input$sidebarItemExpanded), ""))
if (input$sidebarItemExpanded == "tab1") {
ui_code("tab1")
} else {
ui_code("tab2")
}
})
}
# Run the application
shinyApp(ui = ui, server = server)
#Modules.R
ui_code = function (id) {
ns = NS(id)
plotOutput(ns("plot"))
}
bill_species_server = function(id, in_species, in_island, in_mass) {
moduleServer(id, function(input, output, session) {
ns <- session$ns
output$plot = renderPlot({
if (length(in_species()) > 0) {
penguins = penguins[species %in% in_species()]
}
if (length(in_island()) > 0) {
penguins = penguins[island %in% in_island()]
}
penguins = penguins[body_mass_g %between% c(in_mass()[1], in_mass()[2])]
ggplot(penguins) + geom_histogram(aes(x = `bill_length_mm`, fill = species)) + scale_fill_canva(palette = "Striking and energetic")
})
})
}
flipper_mass_scatter_server = function (id, in_species, in_island, in_sex) {
moduleServer(id, function(input, output, session) {
output$plot = renderPlot({
if (length(in_species()) > 0) {
penguins = penguins[species %in% in_species()]
}
if (length(in_island()) > 0) {
penguins = penguins[island %in% in_island()]
}
if (length(in_sex()) > 0) {
penguins = penguins[sex %in% in_sex()]
}
ggplot(penguins) + geom_point(aes(x = `flipper_length_mm`, y = body_mass_g, colour = species)) + scale_colour_canva(palette = "Striking and energetic")
})
})
}

only refresh shiny plot at launch and if action button is clicked

I want to render shiny plots on launch and then require clicking the action button to re-render. I tried to simplify my app to post here. As you can, see changing the "week" selection triggers a refresh. how do I suppress all refreshes unless action Is clicked?
library(shiny); library(dplyr); library(ggplot2)
#toy data
dates= seq.Date(as.Date("2020-01-01"),as.Date("2020-05-01"),by="days")
set.seed(1)
data = data.frame(date = dates,val = runif(length(dates),50,150))
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("group","Group",choices = LETTERS[1:3]),
dateRangeInput('dateRangeCal', "Input date range"),
selectInput("week","shift week",choices = c(0:3)),
actionButton("action","Submit")
),
mainPanel(
plotOutput(outputId = "plot")
)
)
)
server <- function(input, output,session) {
observeEvent( input$action, {
startDate = as.Date("2020-01-01")+days(case_when(
input$group == "A" ~ 0,
input$group == "B" ~ 30,
input$group == "C" ~ 60
))
endDate=startDate+days(60)
updateDateRangeInput(session = session,
inputId = 'dateRangeCal',
label = 'Date range input:',
start = startDate,
end = endDate
)
},ignoreNULL = F)
output$plot <- renderPlot({
p = data %>%
filter(date>=input$dateRangeCal[1]+days(input$week)*7,date<=input$dateRangeCal[2]) %>%
ggplot(.,aes(x=date,y=val))+
geom_line()
p
})
}
shinyApp(ui, server)
That should do it:
server <- function(input, output,session) {
week <- reactiveVal()
observeEvent( input$action, {
week(input$week)
startDate = as.Date("2020-01-01")+days(case_when(
input$group == "A" ~ 0,
input$group == "B" ~ 30,
input$group == "C" ~ 60
))
endDate=startDate+days(60)
updateDateRangeInput(session = session,
inputId = 'dateRangeCal',
label = 'Date range input:',
start = startDate,
end = endDate
)
},ignoreNULL = F)
output$plot <- renderPlot({
p = data %>%
filter(date>=input$dateRangeCal[1]+days(week())*7,date<=input$dateRangeCal[2]) %>%
ggplot(.,aes(x=date,y=val))+
geom_line()
p
})
}
Would this work?
library(shiny); library(dplyr); library(ggplot2); library(lubridate)
#toy data
dates= seq.Date(as.Date("2020-01-01"),as.Date("2020-05-01"),by="days")
set.seed(1)
data = data.frame(date = dates,val = runif(length(dates),50,150))
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("group","Group",choices = LETTERS[1:3]),
dateRangeInput('dateRangeCal', "Input date range"),
selectInput("week","shift week",choices = c(0:3)),
actionButton("action","Submit")
),
mainPanel(
plotOutput(outputId = "plot")
)
)
)
server <- function(input, output,session) {
observeEvent(input$action, {
grp <- isolate(input$group)
startDate = as.Date("2020-01-01")+days(case_when(
grp == "A" ~ 0,
grp == "B" ~ 30,
grp == "C" ~ 60
))
endDate=startDate+days(60)
updateDateRangeInput(session = session,
inputId = 'dateRangeCal',
label = 'Date range input:',
start = startDate,
end = endDate
)
},ignoreNULL = F)
output$plot <- renderPlot({
input$action
rangecal <- isolate(input$dateRangeCal)
p = data %>%
filter(date>=rangecal[1]+days(isolate(input$week))*7,date<=rangecal[2]) %>%
ggplot(.,aes(x=date,y=val))+
geom_line()
p
})
}
shinyApp(ui, server)

How to put 2 possibles eventReactive in only one variable

I am building a Shiny app which generate a dataframe from a database through the specific function my_function.
I want to use an eventReactive() to attribute the result of my_function depending on different inputs. My problem is that there are 2 ways to select these inputs which are structured in 2 different panels (I need this structure), so I have 2 actionButton that allow me to run my_function, and 1 variable for each eventReactive. Is there a way to put them in only 1 variable ?
df_all is a dataframe with several columns like "VAR1", "YEAR", "TYPE", "AGE" ... I need to filter depending on the inputs.
For the moment I have tried :
library(shiny)
library(shinydashboard)
library(DT)
library(dplyr)
df_all <- data.frame(
VAR1 = c(rep("A", 2), "B", "C")
YEAR = (rep(2001, 3), 2002)
TYPE = c("t1", "t2", "t2", "t1")
)
my_function <- function(arg1, arg2, arg3)
{
df = data.frame(
v1 = paste(arg1, arg2)
v2 = arg3
)
return(df)
}
shinyUI(dashboardPage(
dashboardHeader("title"),
dashboardSidebar(
sidebarMenu(id = "menu",
menuItem("Item1", tabName = "item1")
)),
dashboardBody(
tabItems(
tabItem(tabName = "item1",
selectInput(inputId = "var1", label = NULL, choices = c("A", "B", "C")),
tabsetPanel(
tabPanel("Item1-Panel1",
uiOutput("ui_year1"),
uiOutput("ui_type1"),
div(actionButton(inputId = "extra1", label = "Run", icon = icon("play")))),
tabPanel("Item1-Panel2",
uiOutput("ui_year2"),
uiOutput("ui_type2"),
div(actionButton(inputId = "extra2", label = "Run", icon = icon("play")))),
tabPanel("Item1-Panel3",
DT::dataTableOutput("tableau_ext1"),
DT::dataTableOutput("tableau_ext2"),
downloadButton("downloadCSV", "Save (CSV)"))
))))))
shinyServer(function(input, output) {
output$ui_year1 <- renderUI({
checkboxGroupInput(inputId = "year1", label = NULL, choices = df_all %>% filter(CULTURE == input$var1) %>% select(YEAR) %>% distinct() %>% pull()
})
output$ui_type1 <- renderUI({
checkboxGroupInput(inputId = "type1", label = NULL, choices = sort(df_all %>% filter(VAR1 == input$cult, YEAR %in% input$year1) %>% select(TYPE) %>% distinct() %>% pull())
})
output$ui_year2 <- renderUI({
checkboxGroupInput(inputId = "year2", label = NULL, choices = df_all %>% filter(VAR1 == input$var1) %>% select(YEAR) %>% distinct() %>% pull()
})
output$ui_type2 <- renderUI({
checkboxGroupInput(inputId = "type2", label = NULL, choices = sort(df_all %>% filter(VAR1 == input$cult, YEAR %in% input$year2) %>% select(TYPE) %>% distinct() %>% pull())
})
df1 <- eventReactive(input$extra1, {
my_function(arg1 = input$cult,
arg2 = as.numeric(input$year1),
arg3 = as.character(input$type1))
})
df2 <- eventReactive(input$extra2, {
my_function(arg1 = input$cult,
arg2 = as.numeric(input$year2),
arg3 = as.character(input$type2))
})
})
I tried to attribute the 2 eventReactive in 1 variable df, because I want to see and save the dataframe generated by my_function with :
shinyServer([...]
df <- eventReactive(input$extra1, {
my_function(arg1 = input$cult,
arg2 = as.numeric(input$year1),
arg3 = as.character(input$type1))
})
df <- eventReactive(input$extra2, {
my_function(arg1 = input$cult,
arg2 = as.numeric(input$year2),
arg3 = as.character(input$type2))
})
output$tableau_ext1 <- DT::renderDataTable({
df()
})
output$downloadCSV <- downloadHandler(
filename = function() {
paste0(input$year1, "_", input$type1, ".csv")
},
content = function(file) {
write.csv2(df(), file, row.names = FALSE)
}
)
)
But it didn't worked... If someone knows how to solve my problem, I will be grateful for his help :)
Building off of this thread the following seems to achieve the desired behavior (if I understand everything correctly):
library(shiny)
my_fun <- function() {
x <- sample(x=nrow(iris), size = 6)
x
}
ui <- fluidPage(
tabsetPanel(
tabPanel(title = "panel1",
actionButton("go1", "go 1")),
tabPanel(title = "panel2",
actionButton("go2", "go 2"))
),
mainPanel(dataTableOutput("tab"))
)
server <- function(input, output) {
df <- eventReactive(c(input$go1, input$go2), {
iris[my_fun(),]
}, ignoreNULL = FALSE, ignoreInit = TRUE)
output$tab <- renderDataTable({
df()
})
}
shinyApp(ui, server)
See also ?eventReactive for the ignoreNULL and ignoreInit options.
Edit: Two functions, one eventReactive, and keep track of tabs to know what to render.
library(shiny)
library(dplyr)
go1_fun <- function() {
x <- filter(iris, Species == "setosa") %>% head
x
}
go2_fun <- function() {
x <- filter(iris, Species == "virginica") %>% head
x
}
ui <- fluidPage(
tabsetPanel(id = "tabs",
tabPanel(title = "panel1",
actionButton("go1", "go 1")),
tabPanel(title = "panel2",
actionButton("go2", "go 2"))
),
mainPanel(dataTableOutput("tab"))
)
server <- function(input, output, session) {
df1 <- reactive({
if (req(input$go1)) {
x <- go1_fun()
}
return(x)
})
df2 <- reactive({
if (req(input$go2)) {
x <- go2_fun()
}
return(x)
})
tab_to_render <- eventReactive(c(input$go1, input$go2), {
if (input$tabs == "panel1") x <- df1()
if (input$tabs == "panel2") x <- df2()
return(x)
}, ignoreNULL = FALSE, ignoreInit = TRUE)
output$tab <- renderDataTable({
tab_to_render()
})
}
shinyApp(ui, server)

Resources