Super Solutions for Shiny Apps #3: Softcoding Constants in the App
Two methods for keeping your Shiny app organized while avoiding hardcoding.
Softcoding Constants in a Shiny App
They can be found everywhere. Text on buttons, urls to be linked, some numeric thresholds, a font to be used on ggplot, technical IDs to business names mapping, column names from datasets… We are all (at least try to be) good programmers and we do not want to hardcode those values in the code. You can imagine the inconvenience of searching for them throughout the code when modification is implemented. And you always miss something that slightly does not fit your search query. That is why Shiny developers use so-called constant values (or global variables) in the app.
Let us share two methods that we consider the most useful: a simple and an advanced one. Keep It Simple Stupid (KISS) is usually the way to go, but we cannot stop ourselves from bragging about the advanced solution.
Note: if your constants consist basically of text displayed on the user interface, it might be worth it to introduce internationalization to the app. We recommend the shiny.i18n open source package. It will solve the problem of keeping text organized and not hardcoded as well as prepare you for your manager’s inevitable last minute message: “Could we translate app UI to Hungarian? There is a big client there and we need it for a demo tomorrow. I’m sure you will figure something out.”
The simple solution:
Add an R script with all constants declared as variables (objects). Store them in a single place, making them easily found and managed. What is then needed is simple sourcing of the file (or just having it in an /R directory if you develop Shiny as a package). Keeping them all in global or server is not recommended as those important files will quickly get messy.
What we also recommend is having some special naming convention for those global variables. There is nothing worse than spotting a variable in the code and being confused for a few minutes before realizing that it is not defined in the current observe, but is taken from constants. Any kind of visual distinction can be used, like common prefix, suffix, using different styling e.g. snake_case not camelCase, or, as usually used in programming, spelling such variables with CAPITAL letters. Just be sure to keep the method consistent across the whole project.
Note: using such constants in the app with modules violates the rule of having modules independent from the rest of the code. From our experience modules developed for advanced applications are rarely used in other code, but if you’re afraid of this, then you can store constants on a list per module and pass this list as a module’s parameter. When the module is later used it will be super easy to spot and re-use all of the constants values needed for this module. Here is an example:
The advanced solution:
For the most complicated apps that we have produced, we organized our constants in a little bit more of a sophisticated way. Our goal was to separate namespaces so that variable names do not interfere. In order to achieve it, create an R6Class initialized in global.R (with the built-in $new() method). The class should have only a single public method for initialization: read the bunch of json files (separated according to the functionality/screen) and assign them to the list (named as json file) on the newly created namespace. The jsons are organized as pairs variable-value.
Please note that the json files can be as nested as you wish! That is super helpful for organizing your constants in a hierarchical structure.
If you do not want to mess with namespaces, you can always set consts as a regular list thus implementing some combination of simple and advanced solutions.
To sum up: choosing the method to organize your constants is not as important as avoiding hardcoding in the app. Start with this idea in mind and our simple solution. The future version of You will be grateful!
And don’t forget to sign up for our newsletter!