knitr::opts_chunk$set(collapse = TRUE, comment = "#>", eval = FALSE)
{golem} organises a Shiny application as a package with one UI and one
server, assembled in app_ui.R and app_server.R and launched by run_app().
A brochure app has no single UI and no single server: it has one of each per
page. The two fit together well, but the shape of the app changes, and this
article is about that shape rather than about the two functions brochure
exports — those are in ?golem_hook and ?new_page.
| golem, as usual | golem with brochure |
| --- | --- |
| app_ui.R assembles the whole interface | gone; each page brings its own UI |
| app_server.R holds the whole server | gone; each page brings its own server |
| modules are pieces of one page | a module is a page, plus a page() around it |
| run_app() calls shinyApp() | run_app() calls brochureApp() |
golem_add_external_resources() stays, and matters more than before: it is the
one place declaring your css, javascript and favicon, and it has to reach every
page.
golem::create_golem(file.path(tempdir(), "myapp"), project_hook = brochure::golem_hook)
The hook removes app_ui.R and app_server.R, writes a run_app() built on
brochureApp(), and drops in a first page, R/mod_home.R. It also rewrites the
golem::add_module() calls in dev/02_dev.R so they use the brochure module
template — the file you work from already does the right thing.
golem::add_module(name = "contact", module_template = brochure::new_page)
You get a file holding three things: a module UI, a module server, and a
function wrapping both into a page():
mod_contact_ui <- function(id) { ns <- NS(id) tagList( h1("Hello {brochure}!") ) } mod_contact_server <- function(id) { moduleServer(id, function(input, output, session) { ns <- session$ns }) } contact <- function(id = "contact", href = "/contact") { page( href = href, ui = mod_contact_ui(id = id), server = function(input, output, session) { mod_contact_server(id = id) } ) }
That last function is what you add to run_app(). The module keeps its
namespace, so two pages may reuse the same module under different ids and
hrefs — a mod_profile mounted once at /me and once at /user/:id.
run_app <- function( onStart = NULL, options = list(), enableBookmarking = NULL, ... ) { with_golem_options( app = brochureApp( # Injected into every page: css, javascript, favicon golem_add_external_resources(), # One entry per page home(), contact(), onStart = onStart, options = options, enableBookmarking = enableBookmarking ), golem_opts = list(...) ) }
golem_add_external_resources() goes in ..., among the pages, because
brochureApp() injects everything there that is not a page or a redirection
into all of them. Put it inside a single page() and only that page gets your
stylesheet.
R/app_server.R, and keep only golem_add_external_resources() from
R/app_ui.Rpage(), the way new_page() lays
it outshinyApp() with brochureApp() in run_app(), passing
golem_add_external_resources() and then the pagesStep 2 is where the work is, and it is rarely mechanical. Anything the old
app_server.R held to pass state between screens has no equivalent: a page
cannot read another page's reactive values, because they never exist at the
same time. That state has to move to a cookie plus a store — see
vignette("cookies").
golem::get_golem_options(), the config file, app_sys(), the dev/ scripts,
golem::add_dockerfile(): all of it works unchanged. run_app() is still the
entry point, and the app is still a package.
Testing changes a little: there is no single app_server() to drive with
shiny::testServer(). Test each page's module server as you would any module,
and drive the app's routing through its httpHandler with a request built by
hand. vignette("testing") covers both.
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.