I am working on a Shiny-app and since it is a rather large project, I am splitting it up in several files.
I have these app.R, ui.R and server.R:
app.R
library(shiny)
library(shinydashboard)
shinyApp(ui, server)
ui.R
library(shiny)
library(shinydashboard)
library(shinyWidgets)
header <- dashboardHeader(disable = TRUE)
sidebar <- dashboardSidebar(
sidebarMenu(id = "tabs",
sidebarMenuOutput("menu"))
)
MainBox <- fluidRow(
titlePanel("PromView"),
tabItems(
tabItem(tabName = "tab1", QuickPlot()),
tabItem(tabName = "tab2", p("Doorloop Content"))
)
)
body <- dashboardBody(MainBox)
ui <- dashboardPage(title = "PromView", skin = "blue",
header,
sidebar,
body
)
server.R
library(plotly)
source("~/Werk/BigData/Process Mining/R/PM_Globals.R", local = TRUE)
source("~/Werk/BigData/Process Mining/PromView/PromView/QuickFacts.R", local = TRUE)
source("~/Werk/BigData/Process Mining/PromView/PromView/LoadData.R", local = TRUE)
source("~/Werk/BigData/Process Mining/PromView/PromView/Filters.R", local = TRUE)
server <- function(input, output, session) {
output$menu <- renderMenu({
sidebarMenu(
menuItem("Quick facts", tabName = "tab1"),
menuItem("Doorloop", tabName = "tab2")
)
})
isolate({updateTabItems(session, "tabs", "tab1")})
}
When I start the app (in Rstudio), the app complains that it can' t find the QuickPlot()-function. The environment-pane in Rstudio is empty.
When I manually execute the "source"-lines in server.R, the environment-pane shows that all data are loaded and that functions are defined. The app then starts normally.
How can I force shiny to use the " source"-lines?
Ben
You can put the "source"-lines in the ui.R file, shiny loads ui file before server file and therefore, your app will complain that there is no QuickPlot function.
Related
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)
The app below contains a module that renders a vertical tab panel in the UI. I call this module twice in the app.
The first instance of the module renders correctly:
However, the second instance does not (the content of the tabs is no longer visible):
If you check out the inspector, you can see that for the first instance, the HTML dependencies are appended to the tabset:
However, these dependencies are missing for the second instance:
I'm guessing this is due to the use of singleton() in the source code for verticalTabsetPanel? I don't know how to get around this. I tried adding the dependencies in the \www folder in my app directory and then manually prepending them to the head of my app but that doesn't seem to work either, any insight as to why would be greatly appreciated.
Here is the code:
library(shiny)
library(shinyWidgets)
library(shinydashboard)
modui <- function(id) {
ns <- NS(id)
tagList(
verticalTabsetPanel(
id = ns('tabs'),
verticalTabPanel(ns('item1'), ns('item1 content')),
verticalTabPanel(ns('item2'), ns('item2 content'))
)
)
}
modserver = function(input, output, session) {
}
# HEADER ------------------------------------------------------------------
header <- dashboardHeader()
# SIDEBAR -----------------------------------------------------------------
sidebar <- dashboardSidebar(
sidebarMenu(
menuItem("Tab 1", tabName = 'tab_1'),
menuItem("Tab 2", tabName = 'tab_2'))
)
# BODY --------------------------------------------------------------------
body <- dashboardBody(
tags$head(
tags$link(rel = "stylesheet", type = "text/css", href = "vertical-tab-panel.css"),
tags$script(src='vertical-tab-panel.js'),
tags$script(src='vertical-tab-panel-bindings.js')
),
tabItems(
tabItem(tabName = 'tab_1', modui('t1')),
tabItem(tabName = 'tab_2', modui('t2')) )
)
# UI ----------------------------------------------------------------------
ui = dashboardPage(header, sidebar, body)
shinyApp(
ui = ui,
server = function(input, output, session) {
callModule(modserver, 't1')
callModule(modserver, 't2')
}
)
Background
Within a modular1 Shiny application, I would like to load module only when menu item on shinydashboard is clicked. If the menu item is not accessed I wouldn't like to load the module.
Basic application
app.R
# Libs
library(shiny)
library(shinydashboard)
# Source module
source("sample_module.R")
ui <- dashboardPage(
dashboardHeader(title = "Dynamic sidebar"),
dashboardSidebar(sidebarMenuOutput("menu")),
dashboardBody(tabItems(
tabItem(tabName = "tab_one", h1("Tab One")),
tabItem(tabName = "tab_two", sampleModuleUI("sampleModule"))
))
)
server <- function(input, output) {
callModule(sampleModuleServer, "sampleModule")
output$menu <- renderMenu({
sidebarMenu(
menuItem(
"Menu item 1",
icon = icon("calendar"),
tabName = "tab_one"
),
menuItem(
"Menu item 2",
icon = icon("globe"),
tabName = "tab_two"
)
)
})
}
shinyApp(ui, server)
sample_module.R
sampleModuleServer <- function(input, output, session) {
output$plot1 <- renderPlot({
plot(mtcars)
})
}
sampleModuleUI <- function(id) {
ns <- NS(id)
plotOutput(ns("plot1"))
}
Desired implementation
The desired implementation would load sample_module only when the relevant menu item is clicked. On the lines of 2:
Don't call callModule from inside observeEvent; keep it at the top level. Take the reactive expression that's returned, and use eventReactive to wrap it in the button click. And use the eventReactive from your outputs, etc.
x <- callModule(...)
y <- eventReactive(input$go, x())
output$tbl <- DT::renderDataTable(y())
Attempt
app.R (modified)
# Libs
library(shiny)
library(shinydashboard)
# Source module
source("sample_module.R")
ui <- dashboardPage(
dashboardHeader(title = "Dynamic sidebar"),
dashboardSidebar(sidebarMenuOutput("menu")),
dashboardBody(tabItems(
tabItem(tabName = "tab_one", h1("Tab One")),
tabItem(tabName = "tab_two", sampleModuleUI("sampleModule"))
))
)
server <- function(input, output) {
eventReactive(eventExpr = input$tab_two,
valueExpr = callModule(sampleModuleServer, "sampleModule")
)
output$menu <- renderMenu({
sidebarMenu(
menuItem(
"Menu item 1",
icon = icon("calendar"),
tabName = "tab_one"
),
menuItem(
"Menu item 2",
icon = icon("globe"),
tabName = "tab_two"
)
)
})
}
shinyApp(ui, server)
Problem
Application runs but the module does not load. Questions:
How to correctly call eventReactive on dashboard menu item? The tab_item does not seem to have id parameter is tabName equivalent in that context?
The linked discussion refers to refreshing one table. I'm trying to figure out example that will work with modules containing numerous interface element and elaborate server calls.
Clicking on Menu item 2 should display the content from the sample_module.R file.
1 Modularizing Shiny app code
2 Google groups: activate module with actionButton
Update
I've tried explicitly forcing module into application environment load using the following syntax:
eventReactive(eventExpr = input$tab_two,
valueExpr = callModule(sampleModuleServer, "sampleModule"),
domain = MainAppDomain
)
where
MainAppDomain <- getDefaultReactiveDomain()
Edit: Dropping Joe Cheng's top level statement:
# Libs
library(shiny)
library(shinydashboard)
# Source module
source("sample_module.R")
ui <- dashboardPage(
dashboardHeader(title = "Dynamic sidebar"),
dashboardSidebar(sidebarMenuOutput("menu")),
dashboardBody(tabItems(
tabItem(tabName = "tab_one", h1("Tab One")),
tabItem(tabName = "tab_two", sampleModuleUI("sampleModule"))
))
)
server <- function(input, output) {
observeEvent(input$tabs,{
if(input$tabs=="tab_two"){
callModule(sampleModuleServer, "sampleModule")
}
}, ignoreNULL = TRUE, ignoreInit = TRUE)
output$menu <- renderMenu({
sidebarMenu(id = "tabs",
menuItem(
"Menu item 1",
icon = icon("calendar"),
tabName = "tab_one"
),
menuItem(
"Menu item 2",
icon = icon("globe"),
tabName = "tab_two"
)
)
})
}
shinyApp(ui, server)
Furthermore, your sidebarMenu needs an id to access the selected tabs; please see the shinydashboard documentation.
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(
....
I am trying to build a shiny Dashboard and I would like to use uiOutput.
In my ui.R file in the dashboardSidebar I have a menuitem that calls uiOutput:
library(shiny)
dashboardSidebar(
sidebarMenu(
menuItem("TCA", tabName = "dashboard", icon = icon("dashboard")),
menuItem(uiOUtput("Symbols")) ....
my server.r file looks like:
output$Symbols<-renderUI({
selectInput('Test', 'Test:', choices = c(1,2,3), selected = 1)
})
When I run the app:
## app.R ##
library(shinydashboard)
library(shiny)
shinyApp(ui, server)
I get an error:
Error in tag("span", list(...)) : could not find function "uiOUtput"
Do you know how to use shinyDashboard with uiOutput?
Once you correct the typo the following works for me. You should be able to use uiOuput.
library(shinydashboard)
runApp(
list(ui = dashboardPage(
dashboardHeader(),
dashboardSidebar(
sidebarMenu(
menuItem(uiOutput("Symbols"))
)
),
dashboardBody()
)
, server = function(input, output) {
output$Symbols<-renderUI({
selectInput('Test', 'Test:', choices = c(1,2,3), selected = 1)
})
}
)
)