Taking Control of Reactivity, Inputs, and Outputs

Shiny and R were made for each other, and this tutorial will show you how to make the most of them. In no time, you’ll be producing fully-featured interactive data summaries to share over the web.

(For more resources related to this topic, see here.)

Showing and hiding elements of the UI

We'll start easy with a simple function that you are certainly going to need if you build even a moderately complex application. Those of you who have been doing extra credit exercises and/or experimenting with your own applications will probably have already wished for this or, indeed, have already found it. conditionalPanel() allows you to show/hide UI elements based on other selections within the UI. The function takes a condition (in JavaScript, but the form and syntax will be familiar from many languages) and a UI element, and displays the UI only when the condition is true. This is actually used a couple of times in the advanced GA application and indeed in all the applications I've ever written of even moderate complexity. The following is a simpler example (from ui.R, of course, in the first section, within sidebarPanel()), which allows users who request a smoothing line to decide what type they want:

conditionalPanel(
condition = "input.smoother == true",
selectInput("linearModel", "Linear or smoothed",
list("lm", "loess"))
)

As you can see, the condition appears very R/Shiny-like, except with the "." operator familiar to JavaScript users in place of "$", and with "true" in lower case. This is a very simple but powerful way of making sure that your UI is not cluttered with irrelevant material.

Giving names to tabPanel elements

In order to further streamline the UI, we're going to hide the hour selector when the monthly graph is displayed and the date selector when the hourly graph is displayed. The difference is illustrated in the following screenshot with side-by-side pictures, hourly figures UI on the left-hand side and monthly figures on the right-hand side:

In order to do this, we're going to have to first give the tabs of the tabbed output names. This is done as follows (with the new code in bold):

tabsetPanel(id ="theTabs",
tabPanel("Summary", textOutput("textDisplay"),
value = "summary"),
tabPanel("Monthly figures",
plotOutput("monthGraph"), value = "monthly"),
tabPanel("Hourly figures",
plotOutput("hourGraph"), value = "hourly")
)

As you can see, the whole panel is given an ID (theTabs), and then each tabPanel is also given a name (summary, monthly, and hourly). They are referred to in the server.R file very simply as input$theTabs. Let's have a quick look at a chunk of code in server.R that references the tab names; this code makes sure that we subset based on date only when the date selector is actually visible, and by hour only when the hour selector is actually visible. Our function to calculate and pass data now looks like the following (new code again bolded):

passData <- reactive({
if(input$theTabs != "hourly"){
analytics <- analytics[analytics$Date %in%
seq.Date(input$dateRange[1], input$dateRange[2],
by = "days"),]
}
if(input$theTabs != "monthly"){
analytics <- analytics[analytics$Hour %in%
as.numeric(input$minimumTime) :
as.numeric(input$maximumTime),]
}
analytics <- analytics[analytics$Domain %in%
unlist(input$domainShow),]
analytics
})

As you can see, subsetting by month is carried out only when the date display is visible (that is, when the hourly tab is not shown), and vice versa.

Finally, we can make our changes to ui.R to remove parts of the UI based on tab selection:

conditionalPanel(
condition = "input.theTabs != 'hourly'",
dateRangeInput(inputId = "dateRange",
label = "Date range",
start = "2013-04-01",
max = Sys.Date()
)
),
conditionalPanel(
condition = "input.theTabs != 'monthly'",
sliderInput(inputId = "minimumTime",
label = "Hours of interest- minimum",
min = 0,
max = 23,
value = 0,
step = 1
),
sliderInput(inputId = "maximumTime",
label = "Hours of interest- maximum",
min = 0,
max = 23,
value = 23,
step = 1)
)

Note the use in the latter example of two UI elements within the same conditionalPanel() call; it is worth noting that it helps you keep your code clean and easy to debug.

Reactive user interfaces

Another trick you will definitely want up your sleeve at some point is a reactive user interface. This enables you to change your UI (for example, the number or content of radio buttons) based on reactive functions. For example, consider an application that I wrote related to survey responses across a broad range of health services in different areas. The services are related to each other in quite a complex hierarchy, and over time, different areas and services respond (or cease to exist, or merge, or change their name...), which means that for each time period the user might be interested in, there would be a totally different set of areas and services. The only sensible solution to this problem is to have the user tell you which area and date range they are interested in and then give them back the correct list of services that have survey responses within that area and date range.

The example we're going to look at is a little simpler than this, just to keep from getting bogged down in too much detail, but the principle is exactly the same and you should not find this idea too difficult to adapt to your own UI. We are going to imagine that your users are interested in the individual domains from which people are accessing the site, rather than just have them lumped together as the NHS domain and all others. To this end, we will have a combo box with each individual domain listed. This combo box is likely to contain a very high number of domains across the whole time range, so we will let users constrain the data by date and only have the domains that feature in that range return. Not the most realistic example, but it will illustrate the principle for our purposes.

Reactive user interface example – server.R

The big difference is that instead of writing your UI definition in your ui.R file, you place it in server.R, and wrap it in renderUI(). Then all you do is point to it from your ui.R file. Let's have a look at the relevant bit of the server.R file:

output$reacDomains <- renderUI({
domainList = unique(as.character(passData()$networkDomain))
selectInput("subDomains", "Choose subdomain", domainList)
})

The first line takes the reactive dataset that contains only the data between the dates selected by the user and gives all the unique values of domains within it. The second line is a widget type we have not used yet which generates a combo box. The usual id and label arguments are given, followed by the values that the combo box can take. This is taken from the variable defined in the first line.

Reactive user interface example – ui.R

The ui.R file merely needs to point to the reactive definition as shown in the following line of code (just add it in to the list of widgets within sidebarPanel()):

uiOutput("reacDomains")

You can now point to the value of the widget in the usual way, as input$subDomains. Note that you do not use the name as defined in the call to renderUI(), that is, reacDomains, but rather the name as defined within it, that is, subDomains.

Summary

It's a relatively small but powerful toolbox with which you can build a vast array of useful and intuitive applications with comparatively little effort.

This article looked at fine-tuning the UI using conditionalPanel() and observe(), and changing our UI reactively.

Resources for Article:


Further resources on this subject:


Books to Consider

comments powered by Disqus