I have a shiny app (bundled as part of a package) where at the beginning of the server function I create a bunch of reactive data frames that later get used in other parts of the app.
Since quite a few dataframes get created, I wanted to make a simple setup_data() function that could be called at the beginning, in order to help keep the app code tidy. However, since the dfs are created inside a function, I need to use either <<- or assign to make sure they're available in Shiny's server environment.
library(shiny)
setup_data <- function(){
reactiveDat1 <<- shiny::reactiveValues()
reactiveDat1$mydf <<- data.frame(x = 1, y = 2)
reactiveDat2 <<- shiny::reactiveValues()
reactiveDat2$mydf <<- data.frame(x = 5, y = 10)
}
ui <- fluidPage(
titlePanel(""),
sidebarLayout(
sidebarPanel(
),
mainPanel(
)
)
)
server <- function(input, output) {
setup_data()
# rest of app goes here....
}
# Run the application
shinyApp(ui = ui, server = server)
Doing it this way generates a cran NOTE of no visible binding for '<<-' assignment, and in general it is bad practise to do global assigns in a cran package.
Therefore, is there way I can create a function that does the setup like this, but in a way conducive to shiny and cran packages? Ideally I'd like to avoid returning everything in a list, and I haven't found a way to make this work in the Shiny Modules framework as there is no corresponding UI to tie these to.
Is there any other options?
You can setup the data using the global.R file. Any R objects that are created in the global.R file become available to the app.R file, or the ui.R and server.R files respectively. Take a look at how to modularize shiny apps.
EDIT: As pointed out in the comments, you can use local = TRUE to only load objects into the shiny environment.
Related
Introduction
I have created an R shiny dashboard app that is quickly getting quite complex. I have over 1300 lines of code all sitting in app.R and it works. I'm using RStudio.
My application has a sidebar and tabs and rather than using modules I dynamically grab the siderbar and tab IDs to generate a unique identifier when plotting graphs etc.
I'm trying to reorganise it to be more manageable and split it into tasks for other programmers but I'm running into errors.
Working Code
My original code has a number of library statements and sets the working directory to the code location.
rm(list = ls())
setwd(dirname(rstudioapi::getActiveDocumentContext()$path))
getwd()
I then have a range of functions that sit outside the ui/server functions so are only loaded once (not reactive). These are all called from within the server by setting the reactive values and calling the functions from within something like a renderPlot. Some of them are nested, so a function in server calls a function just in regular app.R which in turn calls another one. Eg.
# Start of month calculation
som <- function(x) {
toReturn <- as.Date(format(x, "%Y-%m-01"))
return(toReturn)
}
start_fc <- function(){
fc_start_date <- som(today())
return(fc_start_date)
}
then in server something like this (code incomplete)
server <- function(input, output, session) {
RV <- reactiveValues()
observe({
RV$selection <- input[[input$sidebar]]
# cat("Selected:",RV$selection,"\r")
})
.......
cat(paste0("modelType: ",input[[paste0(RV$selection,"-modeltype")]]," \n"))
vline1 <- decimal_date(start_pred(input[[paste0(RV$selection,"-modeltype")]],input[[paste0(RV$selection,"-modelrange")]][1]))
vline2 <- decimal_date(start_fc())
.......
Problem Code
So now when I take all my functions and put them into different .R files I get errors indicating the functions haven't been loaded. If I load the source files by highlighting them and Alt-Enter running them so they are loaded into memory then click on Run App the code works. But if I rely on Run App to load those source files, and the functions within them, the functions can't be found.
source('./functionsGeneral.R')
source('./functionsQuote.R')
source('./functionsNewBusiness.R')
source('./ui.R')
source('./server.R')
shinyApp(ui, server)
where ui.R is
source('./header.R')
source('./sidebar.R')
source('./body.R')
source('./functionsUI.R')
ui <- dashboardPage(
header,
sidebar,
body
)
Finally the questions
In what order does R Shiny Dashboard run the code. Why does it fail when I put the exact same inline code into another file and reference it with source('./functions.R')? Does it not load into memory during a shiny app session? What am I missing?
Any help on this would be greatly appreciated.
Thanks,
Travis
Ok I've discovered the easiest way is to create a subfolder called R and to place the preload code into that folder. From shiny version 1.5 all this code in the R folder is loaded first automatically.
I developed a shiny app which displays some dynamic charts. These charts are generated at execution time according to the value of some buttons. This shiny app gets the data from a raw csv which is previously treated and transformed. I got a Rscript apart from the shiny app to do all those "transformations" of the raw data. What I would like to do is to call this Rscript from the shiny app in order to be executed when the shiny app is launched.
I have already checked these links but it didn't help at all: How can I connect R Script with Shiny app in R? and this one using Source() in Shiny. I checked the Rstudio documentation too: http://shiny.rstudio.com/tutorial/lesson5/.
I think it should be something like this, being procesadoDatos.R the RScript. i just want the source command to be executed at the beginning in order to load the data as the shiny app is starting:
source("procesadoDatos.R",local = TRUE)
shinyServer(function(input, output,session) {
(renderplots, reactives elements and so on)}
The Rscript is the shiny project path as the server.R and UI.R files. I also tried including the path but it didn't work either.
Another thing I tried was to create a function which makes all the transformations and then call it from server.R file after sourcing it:
source("procesadoDatos.R",local = TRUE)
generate_data(ticketsByService_report10.csv)
Being generate_data this function defined in the RScript:
generate_data <- function(csv_file) {
(all those transformation, data frame an so on)}
In all cases I got the same error saying that the data frames which are generated in the RScript aren't found.
Does anyone know what is wrong? Thanks in adavance
Scoping in Shiny
All this largely depends on where exactly you call source(). If you need the data to be found from within both the UI and the server function, you put source() outside the app.
If you place source() inside the server function, the UI won't be able to find any object created by the script. If you put this inside a render function, the objects are only visible inside that render function. See also Scoping rules for Shiny
Note that if you have separate server.R and ui.R files and you want the UI to find the objects created by the script, you should add a global.R file to your app directory. The source() command then goes in the global.R file.
A small example:
source('testScript.R')
shinyApp(
fluidPage(
selectInput("cols", "pick columns",
choices = names(x)),
dataTableOutput("what")),
function(input, output, session){
output$what <- renderDataTable(x)
}
)
and testScript.R contains one line:
x <- iris
The key here is:
the script actually has to create these objects
the script should be sourced in the correct spot.
So if you can do the following:
shinyApp(
fluidPage(
selectInput("cols", "pick columns",
choices = names(x)),
dataTableOutput("what")),
function(input, output, session){
source('testScript.R', local = TRUE)
output$what <- renderDataTable(x)
}
)
you get an error about not being able to find x. That's normal, because x is now only defined inside the environment of the server function.
You still can do this though:
shinyApp(
fluidPage(
dataTableOutput("what")),
function(input, output, session){
source('R/testScript.R', local = TRUE)
output$what <- renderDataTable(x)
}
)
Note how x is only needed inside the server function, not inside the UI.
Using functions
For functions, the same thing applies. You put the function definition in a script and source it like before. A function is nothing else but an object, so the script essentially creates a function object that then can be found using the exact same scoping rules.
Keep in mind though that this function should return something if you want to use the result of the function. So put this trivial example in testScript.R:
myfun <- function(x){
tmp <- iris[x]
return(tmp)
}
And now you can do the following:
source('testScript.R', local = TRUE)
shinyApp(
fluidPage(
selectInput("cols", "pick columns",
choices = names(myfun())),
dataTableOutput("what")),
function(input, output, session){
output$what <- renderDataTable(myfun(input$cols))
}
)
This doesn't work any longer if you put the source() inside the server function. The UI side won't be able to see myfun() any more.
rm(list = ls())
shinyApp(
fluidPage(
selectInput("cols", "pick columns",
choices = names(myfun())),
dataTableOutput("what")),
function(input, output, session){
source('R/testScript.R', local = TRUE)
output$what <- renderDataTable(myfun(input$cols))
}
)
# Error in myfun() : could not find function "myfun"
I have recently discovered the rhandsontable package in r and it will be very useful in some of my r shiny projects. I have slightly modified what I saw here Get selected rows of Rhandsontable as a little tester for what I will use this package for.
I want to be able to let users change values of the data frame from within r using the rhandsontable package.So here I want df[1,1] to update each time I change that value. I am just a bit confused when it comes to wrapping a reactive function around render functions especially the renderRHandsontable function. I have used reactive functions with plotting but this is a bit different.
library(shiny)
library(rhandsontable)
ui=fluidPage(
rHandsontableOutput('table'),
verbatimTextOutput('selected'),
verbatimTextOutput("tr")
)
server=function(input,output,session)({
a<-c(1,2)
b<-c(3,4)
c<-rbind(df1,df2)
df1<-data.frame(df3)
#need reactive function around the following
output$table=renderRHandsontable(
rhandsontable(df1,selectCallback = TRUE,readOnly = FALSE)
)
output$selected=renderPrint({
cat('Selected Row:',input$table_select$select$r)
cat('\nSelected Column:',input$table_select$select$c)
cat('\nSelected Cell Value:',input$table_select$data[[input$table_select$select$r]][[input$table_select$select$c]])
df1[input$table_select$select$r,input$table_select$select$c]<-input$table_select$data[[input$table_select$select$r]][[input$table_select$select$c]]
})
#need reactive function around the following
output$tr <- renderText({
df1[1,1]
})
})
# end server
shinyApp(ui = ui, server = server)
It is an interesting area that will open up a lot in my shiny apps for users to play around with.
Thanks
Your code here is not reproducible. In the beginning of your server function, you used rbind() on df1 and df2when neither of these objects exist yet. R will throw an error (and it should!)
Because of that I will have to assume that your data frame is in fact the following:
a<-c(1,2)
b<-c(3,4)
c<-rbind(a,b)
df1<-data.frame(c)
To bind the reactivity output from Rhandsontable to your textOutput, you can use the observe() function from Shiny or even better, the handy hot_to_r function from rhandsontable itself. This function converts the handsontable data to R object.
Without changing your ui function, this will be your server function:
server <- function(input,output,session)({
a<-c(1,2)
b<-c(3,4)
c<-rbind(a,b)
df1<-data.frame(c)
output$table<-renderRHandsontable(
rhandsontable(df1)
)
#use hot_to_r to glue 'transform' the rhandsontable input into an R object
output$tr <- renderText({
hot_to_r(input$table)[1,1]
})
})
Then proceed to call your Shiny app as usual: shinyApp(ui = ui, server = server) and your output$tr now reacts to any edits on your table.
I typically use print when I'm building shinyapps as a way to check to make sure what I think should happen is actually what is happening. However, I've run into a problem where print is no longer printing to the R console when I have an app running. Here's a simple example of what I've tried:
ui <- fluidPage(
sliderInput("slider", "Slider Range",
min = 0, max = 5000, value = c(11))
)
server <- function(input, output){
print("Non-Reactive")
new <- reactive({
print("Reactive")
print(input$slider)
})
}
shinyApp(ui, server)
Within the R console, I get: [1] "Non-Reactive" and that is it. No [1] "Reactive" or [1] 11 for my slider.
I know it used to work before, but I'm not sure what changed. If there's another easier way to check/debug code for shinyapps aside from print I'd love to know my other options too.
Note: My shinyapp package is version 0.13.0. My RStudio version is 0.99.491. R is currently 3.2.2.
As jenesaisquoi mentioned, I had to use observe(new()) later in my server code in order to observe changes in the reactive() function. I must just by dumb luck never used print() in a reactive() function when testing things.
I want to deploy (to the web) an application made in R/Shiny by a call to the shinyApp() function.
It is possible to make an app by a call to ShinyApp() as follows:
test_app = shinyApp(
ui = fluidPage(
numericInput("n", "n", 1),
plotOutput("plot")
),
server = function(input, output) {
output$plot <- renderPlot( plot(head(cars, input$n)) )
}
)
This retuns an object that represents the app and the app can run by printing that object. I wish to deploy the app that is made usingshinyapps::deployApp( test_app) however that gives me the following error:
Error in shinyapps::deployApp(test_app) :
appDir must be a single element character vector
This is because the deployApp function is expecting a directory not a shinyApp object. Presumably the information to build and therefore deploy the app is contained in the test_app object, but inspecting that object does not reveal much, and it seems to be the same for any app I create:
> str(test_app)
List of 4
$ httpHandler :function (req)
$ serverFuncSource:function ()
$ onStart : NULL
$ options : list()
- attr(*, "class")= chr "shiny.appobj"
>
The code to produce the app is not contained in any obvious way in that object. I suspect the answer may have something to do with R6 reference classes, which I do not understand.
Does anybody know how I might extract the information contained in the app from the test_app object in order to deploy it via the deployApp() function? (or an alternative approach)
I have posted this on the shinyApps users google group but got no response, so I'm trying again here.
The shinyApp command is not meant to be used for app construction, from its' help:
You generally shouldn't need to use these functions to create/run
applications; they are intended for interoperability purposes, such as
embedding Shiny apps inside a knitr document.
deployApp doesn't support shinyApp apps, as you probably found from ?deployApp. That said, it's an easy fix for your (and most) apps, by more or less pasting your commands into files called ui.R and server.R, wrapped in shinyUI() and shinyServer():
ui.R:
library(shiny)
shinyUI(fluidPage(
numericInput("n", "n", 1),
plotOutput("plot")
)
)
server.R:
library(shiny)
shinyServer(function(input, output) {
output$plot <- renderPlot( plot(head(cars, input$n)) )
}
)
Put the two files in a directory and then run deployApp("dir") after testing with runApp("dir")
If you have parts of your shiny app that are not part of server or UI (ie data preprocessing), you will need to paste them above the shiny call in the relevant file. If you are calling you shiny app with arguments, you can either hard code them above your shiny call, or integrate them as reactive values in the shiny itself.