16 Define dependencies
16.1 Introduction
The web provides a myriad of relevant open source HTML templates like Colorlib and Creative Tim. Many of the RinteRface packages are actually built on top of those resources. However, some of them may require more efforts to work with shiny, for reasons mentioned in Chapter 3:
- shiny is built on top of Bootstrap 3 (HTML, CSS and Javascript framework), and changing the framework will not be a trivial endeavor. However, shinymaterial and shiny.semantic are good examples that show this is possible.
- shiny relies on jQuery. Consequently, all templates based upon React, Vue and other Javascript framework will not be natively supported. Again, there exist some examples for React with shiny and more generally, the reactR package developed by Kent Russell and Alan Dipert. Chapter 27 provides a general overview.
In the next chapters, we will focus on the pretty tabler.io dashboard template (Figure 16.1). We’ll describe how to create an R wrapper on top of it, thereby making it available for all shiny users.
This chapter was written about two years ago, on top of the 1.0.0-alpha.7 GitHub release (https://github.com/tabler/tabler/tree/14d0c001436b85d2a4533d63680d209affdf774b). If the Tabler library significantly evolved since that date, the way to incorporate it into the shiny ecosystem remains unchanged. Hence, the methods we describe below may be generalized to other templates. We recommend to read this chapter before the next part in 21, during which we present a more automated workflow, if you want to grasp the main concepts.
16.2 Discover the project
The first step of any template adaptation consists of exploring the underlying GitHub repository and look for mandatory elements, like CSS/JS dependencies. This is a similar strategy if you want to incorporate an htmlwidget as well.
As depicted by Figure 16.2, the most important folders are:
- dist, which contains CSS and JS files as well as other libraries like Bootstrap and jQuery. It is also a good moment to look at the version of each dependency that might conflict with Shiny.
- demo is the website folder used for demonstration purpose. This is our source to explore the template capabilities in depth.
The scss and build folder may be used to customize the tabler template directly. However as stated above, directions on how to do so are out of scope for this book.
16.3 Identify mandatory dependencies
Bootstrap 4
, jQuery
, tabler.min.css
and tabler.min.js
are key elements for the template,
contrary to flag icons which are optional (and take a lot of space).
If your goal is to release your template on CRAN, be mindful of the 5 Mb maximum size limit.
From personal experience, I can attest that this is quite challenging to manage.
To inspect dependencies, we proceed as follows:
- Download or clone the GitHub repository.
- Go to the demo folder and open the
layout-dark.html
file. - Open the HTML inspector.
As shown on Figure 16.3 left-hand side, we need to include the tabler.min.css
from the header. If you are not convinced, try to remove it from the DOM and see what happens. jqvmap is actually related to an external visualization plugin used in the demo. Finally the demo.min.css
file is for the demo purpose.
This will not prevent the template from working, so we will skip it for now. So far so good, we only need one file thus!
JavaScript dependencies are shown on the right-hand side and located at the end of the body tag. Because we will not need all chart-related dependencies like apexcharts
, jquery.vmap
and vmap world
, we may safely ignore them. We only retain the Bootstrap 4, jQuery core and tabler.min.js
, in the same order.
16.4 Bundle dependencies
With the help of the htmlDependency()
function, we are going to create our main Tabler HTML dependency containing all assets to allow our template to render properly. In this example, we are going to cheat a bit: instead of handling local files, we will use a CDN (content delivery network) that hosts all necessary Tabler assets. This avoids to include all the necessary files in the R package, as well as in a GitHub repository.
For a production template, that is designed to go on CRAN, we recommend to host files locally, as described in Chapter 21.
library(htmltools)
tabler_cdn <- "https://cdn.jsdelivr.net/npm/tabler@1.0.0-alpha.7/"
tablers_deps <- htmlDependency(
name = "tabler",
version = "1.0.7", # we take that of tabler,
src = c(href = tabler_cdn),
script = "dist/js/tabler.min.js",
stylesheet = "dist/css/tabler.min.css"
)
We advise the reader to create one HTML dependency per element. The Bootstrap version is 4.3.1
. We can also use a CDN:
bs4_cdn <- "https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/"
bs4_deps <- htmlDependency(
name = "Bootstrap",
version = "4.3.1",
src = c(href = bs4_cdn),
script = "js/bootstrap.bundle.min.js"
)
We finally create our dependency manager:
# add all dependencies to a tag. Don't forget to set
# append to TRUE to preserve any existing dependency
add_tabler_deps <- function(tag) {
# below, the order is of critical importance!
deps <- list(bs4_deps, tablers_deps)
attachDependencies(tag, deps, append = TRUE)
}
Notice the dependencies order in the deps
list. This will be exactly the same order in the head
of the HTML page. Some libraries require to be loaded at a specific place, like the Tabler dependencies which must come after Bootstrap.
Let’s see how to use add_tabler_deps()
. We consider a <div>
placeholder and check for its dependencies with findDependencies()
.
Then, we wrap it with add_tabler_deps()
:
tag <- div()
findDependencies(tag)
#> NULL
tag <- add_tabler_deps(div())
findDependencies(tag)
#> [[1]]
#> List of 10
#> $ name : chr "Bootstrap"
#> $ version : chr "4.3.1"
#> $ src :List of 1
#> ..$ href: chr "https://.../bootstrap/4.3.1/js/"
#> $ meta : NULL
#> $ script : chr "bootstrap.bundle.min.js"
#> $ stylesheet: NULL
#> $ head : NULL
#> $ attachment: NULL
#> $ package : NULL
#> $ all_files : logi TRUE
#> - attr(*, "class")= chr "html_dependency"
#>
#> [[2]]
#> List of 10
#> $ name : chr "tabler"
#> $ version : chr "1.0.7"
#> $ src :List of 1
#> ..$ href: chr "https://.../tabler@1.0.0-alpha.7/dist/"
#> $ meta : NULL
#> $ script : chr "js/tabler.min.js"
#> $ stylesheet: chr "css/tabler.min.css"
#> $ head : NULL
#> $ attachment: NULL
#> $ package : NULL
#> $ all_files : logi TRUE
#> - attr(*, "class")= chr "html_dependency"
As shown above, our dependencies are applied to the div, in the correct order. This order is set by the list list(bs4_deps, tablers_deps)
and allows use to avoid potential conflicts. If we try to run this simple tag in a shiny app, we notice that all dependencies are added to the <head>
tag, whereas the original template loads JavaScript dependencies in the <body>
.
Unfortunately, htmltools does not allow developers to distribute dependencies in different places yet.
Here there is no impact, but this might be no-go for templates requiring JavaScript to be placed in the body. In practice, this is challenging to guess and may only be solved by manual testing.
library(shiny)
ui <- fluidPage(tag)
server <- function(input, output, session) {}
shinyApp(ui, server)
Even though the add_tabler_deps()
function may be applied to any tag, we will use it with the core HTML template, that remain to be designed!
Would you like to see if our dependency system works? Let’s meet in the next chapter to design the main dashboard layout.