I'm working on a multi-tab shiny app. I am writing each tab as a module to encapsulate the UI and server logic. I would like to be able to develop the modules independently of the main app; for example to create the UI of a module and check its results without having to navigate to it in the main app.
What I am doing is making a simple Shiny app that displays just a single module. I put the app in a function in the same file as the module. Calling the function then runs the module. Here is a simple example using the slider module from https://github.com/aoles/modules-tutorial:
This works but it seems awkward and it will be more complex for modules with multiple inputs or outputs. Has anyone found a better way to do this? How do you develop modules?
optional test / file that creates the config / app to check the module
it's not a shinytest at this point; it is just to visualize the output and get quick feedback
other files: helper.R, constants.R
The most difficult part is the interface between the modules or with the main app: how is the data from this module sent to (and received by) other modules.
Also, depending on the app, a module can contain only the UI side or the server side (it is not required to include both).
I like to construct my apps so that they can run without shiny. In the engine file of each module I put stand alone functions that I call from the server file. For example, given the inputs the code to create a ggplot does not live inside the server file but in the engine file. Thus, I can source the engine files from different modules, provide inputs for their functions and check the calculations from an R script / markdown file, without calling shiny.
This is a great question. Some resources and thoughts:
Creating standalone helper functions / "engine functions" in separate R files are certainly a great trick. One great way to use these throughout your app is to wrap them up in a package (R's favorite way to share functions), add unit tests with testthat, and load that package when necessary.
Creating standalone modules, as you mentioned. These can then be put into packages or files, and tested / developed independently using testthat or shinytest.
Ultimately, I am hopeful of a function like the following eventually making its way into the shiny package for easier use. The idea is to run a module independently of its parent application. I'm not 100% satisfied with the API, yet, but it's a start. Feel free to use it!
runModule("my_module", ui = sliderUI, server = slider)
I recommend an approach like this one to the testSlider function that you wrote, because it follows functional programming best practices that make it easier to maintain / reason about. All UI / server parameters would be passed to the ui_params and server_params variables. The only difficulty at present is if you wanted to pass a reactive value, that would be difficult in this context.