Shiny how to refresh data loaded before server function - r

I need to develope a shiny interface with many csv loaded in it. Based on my past experience with shiny, I prefer to import this data before the server function, in this way I hope that each session will run faster. The app will be restarted each morning, so data will be daily refreshed.
The problem is that I need to consider an extra refresh during the day, performed manualy with a button that source an external updating script.
I can't (but I hope that is possibele), refresh the data loaded at the start of the app. Below my (dummy) code:
server:
library(shinydashboard)
library(plotly)
library(data.table)
library(dplyr)
path1<-"C:/Users/.../DATA/"
path2<-"C:/Users/../DATA/csv/"
##load dataset at first start
table<-fread(file=paste0(path2,"main.csv"),data.table=FALSE))
shinyServer(function(input, output,session) {
##### refresh data with button####
observeEvent(input$refresh_data,{
source(paste0(path1,"any_script.r"),local = FALSE)
table<<-fread(file=paste0(path2,"main.csv"))
})
#####...ui####
table_r<-reactive({
##obs populate the input for choosing rows to be plotted
obs<-rev(unique(table$anycolumn))
curve_sint<-list(
lotti=obs,
data=obs
)
})
output$obs_ui<-renderUI({
selectInput("input_obs","Please choose the batch:",
choices =table()$obs ,multiple = T)
})
output$plot<-renderPlotly({
table_r()$data%>%
filter(anycolumn==input$input_obs)%>%
plot_ly(
x=~x,
y=~y,
color=~anycolumn,
type="scatter"
)
})
})
ui:
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(
title = "shiny"
),
dashboardSidebar(
width=250,
sidebarMenu(
menuItem(
"plot data"
tabName = "clhc",
icon = NULL
),
menuItem(
"Update data",
icon=icon("gear"),
tabName="update_data"
)
)
),
dashboardBody(
tabItems(
tabItem(
tabName = "clhc",
fluidRow(
column(width=3,
uiOutput("obs_ui")
),
),
fluidRow(
column(
width=12,
fluidRow(
plotlyOutput("plot")
)
)
)
),
tabItem(
tabName = "update_data",
fluidRow(
box(
width=12,
title="Sint HC",
actionButton("refresh_sint_hc","Refresh", icon=icon("refresh"))
)
)
)
)
)
)
I'm sure that the script inside observeevent works fine, because if I put a print(nrow(table)) after the fread I can see that table is correctly refreshed. I can't understand where the new data is stored because from the plot panel is stil available the old data before the update.
Is my attempt completley wrong?

Using <<- will make table accessible globally, and after terminating your shiny app, but you need it to be reactive. Here is a brief example on using a reactiveVal (setting to table1 as default) that gets modified when the actionButton is selected and a new data file is read.
library(shiny)
library(data.table)
table1 <- fread(file = 'atest1.csv')
ui <- fluidPage(
verbatimTextOutput("text"),
actionButton("refresh", "Refresh")
)
server <- function(input, output, session) {
rv <- reactiveVal(table1)
output$text <- renderText({
names(rv())
})
observeEvent(input$refresh, {
print("Refresh")
table1 <<- fread(file = 'atest2.csv')
rv(table1)
})
}
shinyApp(ui, server)

Related

`tabitem` Content Of Conditional `menuitem` Is Showing Only Once in Shiny

I want a navigation menuItem in my shinydashboard to be conditional and shown depending on a condition evaluated in server.R.
To this end, I have a conditionalPanel containing a menuItem defined beside a regular sidebarMenu in ui.R (I use shinymanager to authenticate users):
sidebar <- dashboardSidebar(
width=280,
sidebarMenu(id = "sidebarmenu",
menuItem(...),
menuItem(...,
menuSubItem(...),
menuSubItem(...)
)
),
conditionalPanel(condition = "output.x === 1",
menuItem("title", tabName="tabname")
)
)
body <- dashboardBody(
tabItems(
tabItem(tabName = "id",
fluidPage(
titlePanel("Hello World")
)),
tabItem(tabName="tabname",
titlePanel("mytitle"),
fluidPage(
dataTableOutput(outputId = "table")
)
)
)
ui <- dashboardPage(
dashboardHeader(title = "Hello App", titleWidth=280),
sidebar,
body
)
ui <- secure_app(ui)
In server.R, I switch output.x depending on the logon details of the logged user:
server <- function(input, output, session) {
# login logic: call the server part, check_credentials returns a function to
# authenticate users
res_auth = secure_server(
check_credentials = check_credentials
)
# Define the logon details with a reactive variable
auth_output <- reactive({
reactiveValuesToList(res_auth)
})
output$x = reactive({
auth_output()$role
})
# Generate a data table from the DB to show conditionally
conn = ...
data = load_data(conn, ...)
disconnect(conn)
output$table = dt_render({data})
# All output variables that need to be transferred to the UI should have
# suspendWhenHidden = FALSE:
outputOptions(output, "x", suspendWhenHidden = FALSE)
The problem: the conditional table is shown only once, whenever I want. After this one time, once I navigate away from it, clicking on the conditional menuItem shows no content. The menuItem still appears, which means that output.x === 1 is evaluated properly, but its contents, i.e. the subsequent tabItem, remains hidden.
I have tried isolate to assign output.x, and even fixed it at 1 to no avail. Any leads?
Since the conditionalPanel cannot be put inside the default sidebarMenu, it must be in its stand-alone conditional sidebarMenu, so I must define two sidebarMenus under dashboardSidebar in this case. The following modification solves the problem:
sidebar <- dashboardSidebar(
width=280,
sidebarMenu(id = "sidebarmenu",
menuItem(...),
menuItem(...,
menuSubItem(...),
menuSubItem(...)
)
),
sidebarMenu(id = "conditional_sidebarmenu",
conditionalPanel(condition = "output.x === 1",
menuItem("title", tabName="tabname")
)
)

renderDataTable with actionButton in two shinydashboard tabs

I came across this issue while trying to user shiny::renderUI to generate a data table output via renderDataTable upon clicking an actionButton. This situation works fine until I try to implement two instances of the same thing in separate tabs. In this case, whichever button is clicked first (be it in tab 1 or tab 2) works correctly; but then the other tab's button doesn't produce the data table. Is there a way to get two buttons, in separate shinydashboard tabs, to render data tables independently?
The following shows reproducible code to demonstrate the issue. A small data frame is populated with random values. Clicking the action button calculates new numbers for the data table--but only for the first data table that is rendered.
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(title = "Test example"),
dashboardSidebar(
sidebarMenu(
menuItem("Tab 1", tabName = "tab_1"),
menuItem("Tab 2", tabName = "tab_2")
)
),
dashboardBody(
tabItems(
tabItem("tab_1",
h2("Tab 1"),
fluidRow(
actionButton("do_refresh_tab_1", "Refresh data")
),
fluidRow(
uiOutput("tab1")
)
),
tabItem("tab_2",
h2("Tab 2"),
fluidRow(
actionButton("do_refresh_tab_2", "Refresh data")
),
fluidRow(
uiOutput("tab2")
)
)
)
)
)
server <- function(input, output, session) {
observeEvent(input$do_refresh_tab_1, {
df <- data.frame(value = rnorm(3),
Measurement = rnorm(3),
stringsAsFactors = FALSE)
output$tab1 <- renderUI({
output$temp <- renderDataTable(df)
dataTableOutput("temp")
})
})
observeEvent(input$do_refresh_tab_2, {
df <- data.frame(value = rnorm(3),
Measurement = rnorm(3),
stringsAsFactors = FALSE)
output$tab2 <- renderUI({
output$temp <- renderDataTable(df)
dataTableOutput("temp")
})
})
}
shinyApp(ui, server)
Before we go to the solution, a couple of general rules of thumb.
Avoid, in fact, never put a render call inside another render call.
Never put a render call inside an observe call
Never put a render call inside a reactive call
Each observe, reactive and render call should be standalone and must perform 1 task/function.
The reason why only the first click was working and the second click on the other tab was not, was because you were attempting to create multiple output bindings with the same id (temp).
Every output element must have its own unique id.
Also, using uiOutput and dataTableOutput for this use case is kinda redundant here.
Here is the simplified code,
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(title = "Test example"),
dashboardSidebar(
sidebarMenu(
menuItem("Tab 1", tabName = "tab_1"),
menuItem("Tab 2", tabName = "tab_2")
)
),
dashboardBody(
tabItems(
tabItem("tab_1",
h2("Tab 1"),
fluidRow(
actionButton("do_refresh_tab_1", "Refresh data")
),
fluidRow(
dataTableOutput("table1")
)
),
tabItem("tab_2",
h2("Tab 2"),
fluidRow(
actionButton("do_refresh_tab_2", "Refresh data")
),
fluidRow(
dataTableOutput("table2")
)
)
)
)
)
server <- function(input, output, session) {
output$table1 <- renderDataTable({
req(input$do_refresh_tab_1)
df <- data.frame(value = rnorm(3),
Measurement = rnorm(3),
stringsAsFactors = FALSE)
return(df)
})
output$table2 <- renderDataTable({
req(input$do_refresh_tab_2)
df <- data.frame(value = rnorm(3),
Measurement = rnorm(3),
stringsAsFactors = FALSE)
return(df)
})
}
shinyApp(ui, server)

Refreshing of shiny app in aws server when refresh button is pressed in browser

I Have my shiny app in aws server shown here
library("shiny")
library("shinydashboard")
library("pool")
library(DBI)
pool <- dbPool(drv = RMySQL::MySQL(),dbname = "demo",host = "db.cr7lk1jwjdht.us-east-2.rds.amazonaws.com",username = "kartik",password = "12345678", port = 3306)
mychoices = dbGetQuery(pool,"select available_scenario from scenario_name;")
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
actionButton("create_scenario", "Create Scenario"),
actionButton("load_scenario","load scenario"),
selectInput('n', "available scenarios", choices = mychoices, multiple = TRUE),
verbatimTextOutput("selected")
),
mainPanel(
uiOutput('tabs'),
uiOutput("input"),
uiOutput("inputs"),
uiOutput("inputss")
)
)
)
server <- function(input, output,session) {
observeEvent(input$create_scenario,{
output$input <- renderUI({
mainPanel(
textInput("txtInput","Enter scenario name"),
textOutput("sname"),
actionButton("save","save_scenario")
)
})
output$sname <- renderText({
input$txtInput
})
observeEvent(input$save,{
# conn <- poolCheckout(pool)
# dbSendQuery(conn,"insert into scenario (name) values (", output$sname <- renderText({
# input$txtInput
#}),");")
dd <- data.frame(x = input$txtInput,row.names = FALSE)
print(dd)
dbWriteTable(pool,"available_scenario",dd,append = TRUE)
# values$dd <- rbind(values$dd,data.frame(Enter = input$txtInput))
})
})
output$tabs = renderUI({
if(!is.null(input$n)){
Tabs <- lapply(paste("tab name:", input$n, sep=" "), tabPanel)
do.call(tabsetPanel, Tabs)}
})
observeEvent(input$load_scenario,{
output$inputs <- renderUI({
dashboardPage(
dashboardHeader(title = "Basic dashboard"),
dashboardSidebar(),
dashboardBody(
# Boxes need to be put in a row (or column)
fluidRow(
box(plotOutput("plot1", height = 250)),
box(
title = "Controls",
sliderInput("slider", "Number of observations:", 1, 100, 50)
))))
} )
histdata <- rnorm(500)
output$plot1 <- renderPlot({
data <- histdata[seq_len(input$slider)]
hist(data)
})
})
}
shinyApp(ui, server)
I have a database table where i am storing the data for scenario name.
When i create a scenario named as Scenario-2 in server, it is saving in my database table but when i refresh the browser of shiny app it is not reflecting in the available scenario .
For it to reflect in available scenario i have to reboot the the server.
Any suggestions please.
Any code that is outside of the ui and the server will only run once when the R session first initializes. If you refresh the page or have someone else go to the app, the R session already exists, so that code won't run again. If you want this code to run every time the shiny app URL is visited, you need to place this code inside the server function. In your code, pool and mychoices are being defined outside the ui and server, so you need to move them (or at least mychoices) inside the server function so that it'll be called every time a new shiny session starts.

Shiny - Can dynamically generated buttons act as trigger for an event

I have a shiny code that generates actions buttons from a numericInput and each of those actions buttons generate a plot when clicked using a observeEvent. The problem is that I don't know how to trigger an event with dynamically generated buttons. The workaround I used was to make a observeEvent for each button but if I generate more buttons than the obserEvents I created it won't work.
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(title = "Dynamic selectInput"),
dashboardSidebar(
sidebarMenu(
menuItemOutput("menuitem")
)
),
dashboardBody(
numericInput("go_btns_quant","Number of GO buttons",value = 1,min = 1,max = 10),
uiOutput("go_buttons"),
plotOutput("plot")
)
)
server <- function(input, output, session) {
output$menuitem <- renderMenu({
menuItem("Menu item", icon = icon("calendar"))
})
output$go_buttons <- renderUI({
buttons <- as.list(1:input$go_btns_quant)
buttons <- lapply(buttons, function(i)
fluidRow(
actionButton(paste0("go_btn",i),paste("Go",i))
)
)
})
#Can this observeEvents be triggerd dynamicly?
observeEvent(input[[paste0("go_btn",1)]],{output$plot <-renderPlot({hist(rnorm(100,4,1),breaks = 10)})})
observeEvent(input[[paste0("go_btn",2)]],{output$plot <- renderPlot({hist(rnorm(100,4,1),breaks = 50)})})
observeEvent(input[[paste0("go_btn",3)]],{output$plot <- renderPlot({hist(rnorm(100,4,1),breaks = 100)})})
observeEvent(input[[paste0("go_btn",4)]],{output$plot <- renderPlot({hist(rnorm(100,4,1),breaks = 200)})})
observeEvent(input[[paste0("go_btn",5)]],{output$plot <- renderPlot({hist(rnorm(100,4,1),breaks = 500)})})
}
shinyApp(ui, server)
You can also create observers dynamically. Just make sure that they are created only once, otherwise they will execute several times.
Below is your code modified to create as many observers as buttons. Please note that if an observer for the button already exist, it should not be created. You can customize your observers too, so each observer could have its own behavior.
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(title = "Dynamic selectInput"),
dashboardSidebar(
sidebarMenu(
menuItemOutput("menuitem")
)
),
dashboardBody(
numericInput("go_btns_quant","Number of GO buttons",value = 1,min = 1,max = 10),
uiOutput("go_buttons"),
plotOutput("plot")
)
)
server <- function(input, output, session) {
output$menuitem <- renderMenu({
menuItem("Menu item", icon = icon("calendar"))
})
# to store observers and make sure only once is created per button
obsList <- list()
output$go_buttons <- renderUI({
buttons <- as.list(1:input$go_btns_quant)
buttons <- lapply(buttons, function(i)
{
btName <- paste0("go_btn",i)
# creates an observer only if it doesn't already exists
if (is.null(obsList[[btName]])) {
# make sure to use <<- to update global variable obsList
obsList[[btName]] <<- observeEvent(input[[btName]], {
cat("Button ", i, "\n")
output$plot <-renderPlot({hist(rnorm(100, 4, 1),breaks = 50*i)})
})
}
fluidRow(
actionButton(btName,paste("Go",i))
)
}
)
})
}

Shiny Dashboard - display a dedicated "loading.." page until initial loading of the data is done

I have initial loading of data from the DB in the server.R which takes a few seconds. Until this is done, the page displayed is distorted (wrong data in selection box, and weird placing of the boxes, see below).
I want to display a different page (or at least different content in my first-displayed tab) until the data is completely loaded.
I thought about doing some kind of conditionalPanel using a condition based on a dedicated global variable (initial_loading_done), but wherever I tried placing the conditionalPanel it didn't work.
This is the structure of my UI.R:
shinyUI(
dashboardPage(
dashboardHeader(title = "Title"),
dashboardSidebar(
sidebarMenu(
menuItem("Tab1", tabName = "Tab1",icon = icon("dashboard")),
menuItem("Tab2", tabName = "Tab2", icon = icon("bar-chart-o"))
)
),
dashboardBody(
includeCSS("custom_css.css"),
tabItems(
tabItem(tabName = "Tab1",
fluidRow(<content>),
mainPanel(
fluidRow(<content>)
)
),
tabItem(tabName = "Tab2",
fluidRow(<content>),
mainPanel(
dataTableOutput('my_data_table')
)
)
)
)
)
)
Here's a very simple example using shinyjs package
The idea is to create the loading "page" and the content "page" under different IDs, have the content page initially hidden, and use show() and hide() after the app is ready
library(shiny)
library(shinyjs)
load_data <- function() {
Sys.sleep(2)
hide("loading_page")
show("main_content")
}
ui <- fluidPage(
useShinyjs(),
div(
id = "loading_page",
h1("Loading...")
),
hidden(
div(
id = "main_content",
"Data loaded, content goes here"
)
)
)
server <- function(input, output, session) {
load_data()
}
shinyApp(ui = ui, server = server)
In server I like to use reactiveValues() to store a setupComplete condition. Then, when the data is loaded my setupComplete is set to TRUE.
In the ui we can then assess this setupComplete condition in a conditionalPanel, and only display the content (in my example the three box() widgets).
Here's a working example
## app.R ##
library(shiny)
library(shinydashboard)
library(shinyjs)
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(),
dashboardBody(
actionButton(inputId = "btn_data", label = "Download"),
conditionalPanel(condition = "output.setupComplete",
box( title = "box1" ),
box( title = "box2" ),
box( title = "boc3" )
),
conditionalPanel(condition = "!output.setupComplete",
box( title = "loading"))
)
)
server <- function(input, output) {
rv <- reactiveValues()
rv$setupComplete <- FALSE
## simulate data load
observe({
if(input$btn_data){
df <- data.frame(id = seq(1,200),
val = rnorm(200, 0, 1))
## Simulate the data load
Sys.sleep(5)
## set my condition to TRUE
rv$setupComplete <- TRUE
}
## the conditional panel reads this output
output$setupComplete <- reactive({
return(rv$setupComplete)
})
outputOptions(output, 'setupComplete', suspendWhenHidden=FALSE)
})
}
shinyApp(ui, server)
The code
hidden(
div(
id = "main_content",
"Data loaded, content goes here"
)
doesn't work with tabsetPanel. But if you move the id to the div level it works beautifully. Thanks to shinyjs author Dean Attali for this tip. https://stackoverflow.com/users/4432127/keshete
hidden(
div(id = "mainTabsetPanel",
tabsetPanel(
....

Resources