Clean Up UI Code

Below is sample code from my UI which creates a tab panel. My UI code has grown tremendously and is getting quite difficult to manage.

Is it possible to put this snippet of code in a separate file and then somehow source this in to my UI code?

Any advice on doing that would be very helpful. Thank you

tabPanel("Export .Rdata",
		sidebarLayout(
			sidebarPanel(
				fileInput('file2export', 'Read in .Rdata File', accept=c('.RData', '.Rda')),
				helpText('Select your desired file format for export; .csv for 
					comma separated or .tsv for tab separated.'),
				radioButtons("filetype", "File type:",
					choices = c("csv", "tsv")),
				downloadButton('downloadData', 'Export')
			),
		mainPanel(
			h1('Data Preview'),
			tableOutput('table')
		)
	)
	), # end tabpanel for export

It might be worth looking at shiny modules which were designed specifically for breaking up complex apps in to more manageable chunks.

1 Like

Thanks, @jim89. Modules are mainly for reusable pieces of code. This is slightly different in that I just want to put the tabPanel code into its own file and somehow source it in.

1 Like

You can define a piece of UI logic, save it to a variable, and then call that variable to actually put the UI in place (I do that when building in shinydashboard to help me understand my code better). So you could probably define the logic in a separate file, either saving it as a variable, or just wrapping the logic a a function with no arguments, then source the file and/or use the function in your UI. A module might still work (if I'm correct in thinking that a module doesn't necessarily need to have server logic), but just using a function that creates the UI and calling that might be simpler (but then you're already half way to a module :smile: )

I recommend a strategy similar to the ones that @jim89 suggested:

  1. create a function that calls the tabPanel code
  2. save the function in global.R (or its own separate R file which you source explicitly somewhere in the app)

If the tabPanel code will be reused in the same app then you should isolate it by creating a module instead of a function.

I found this post by @greg to be very helpful in demostrating how a ui can be simplified by grouping ui elements together in a function.

Tangentally, I've also found that once an app is 75% completed (all the features are in place, but there's still some tweaking and cleaning left to do) it's helpful to convert the app into a package and document the functions/modules. More on that here.

2 Likes

To answer your specific question: let's assume you want to save everything within your tabPanel() with the exception of the title in a file named snippet.R., you would call that file as follows:

tabPanel("Export .Rdata",
   source(snippet.R, local = TRUE)[1]
)

Not calling the [1] at the end would evaluate that file to TRUE, rather than actually sourcing the code.

To see an example of what you're asking about, see here for a full app and explanation

1 Like

@daattali and @bogdanrau your suggestions work (almost) perfect. Here is a sample of my server file, which has one issue

options(shiny.maxRequestSize=50*1024^2) # 50 mg limit on file upload
source('Rcode/codePack.R')
source('Rcode/libPack.R')

shinyServer(function(input, output, session) {

	source(file.path("Rcode/serverFiles", "someCode.R"), local = TRUE)$value

}) # end Program

this program reads in a large file, which is why I have the requestSize modified. When the code that lives inside "someCode.R" is actualy in the server file, no problems. But, when I source that code in as in the example above, I run into memory limits suggesting the adjustment to memory is seemingly ignored?

Also, this program runs on a shiny server, so users access this from a URL. Seems that if we run it locally (on my own machine to test), everything works as expected.

But, when run from the shiny server same code runs into memory errors?

So my suggestion works when sourcing scripts related to the UI into ui.r (or other ui scripts). When you're sourcing into server, you should be able to just:

options(shiny.maxRequestSize=50*1024^2) # 50 mg limit on file upload
source(‘Rcode/codePack.R’)
source(‘Rcode/libPack.R’)

shinyServer(function(input, output, session) {

	source(file.path("Rcode/serverFiles", "someCode.R"), local = TRUE)
}) # end Program

Or try without the local = TRUE?

Also, if the 50MB limit works when running locally but not when running in shiny server, it sounds like it's related more to shiny server itself, rather than your scripts.

Thanks, @bogdanrau. To be clear, the 50MB limit does work on the Shiny Server when the actual code is in the server.R file. But, when I source that code in from another file, it seems to not work.

For example, the mem limit is seemingly ignored here:
shinyServer(function(input, output, session) {

source(file.path("Rcode/serverFiles", "someCode.R"), local = 

}) # end Program

But, it works just fine in this instance

shinyServer(function(input, output, session) {

output$stuff <- {all the actual code here}

}) # end Program

What happens if you try source() without local = TRUE? Also, could you post the actual error you're getting?