Introduction

In this second part of the tutorial series we will put the gauges we created in the previous part in a Shiny app and extend our gauge such that we can update the value from Shiny. We assume the reader has a basic understanding of creating shiny apps. For the readers who have no experience using shiny we refer to the turotial series on the Rstudio shiny website here

Creating an empty shiny application

We will use R Studio to create a shiny app template. To do so select "New project.." from the file menu, select "new directory" and then "Shiny web application". In the next screen fill in "GaugeApp" as directory name and select a subdirectory where you want the app to be created.

Next we modify the default contents of the files created such that we have an empty skeleton. We modify server.R slightly such that we have access to the shiny session object. We will need this later on. The code of server.R will look like this:

library(shiny)

shinyServer(function(input, output, session) {

})

We will next put an empty navbar page in ui.R

library(shiny)

shinyUI(navbarPage(id="main", windowTitle = "Friss", position = "fixed-top", title = NULL,

        tabPanel("panel 1", icon = icon("dashboard")
)))

Notice how both files contain library(shiny) to load the shiny package. This is necessary beacause there is no guarantee which file is loaded first. A solution to this is to create a file global.R. The contents of this file are guaranteed to run before ui.R or server.R are run. We can create a file global.R in the same folder as server.R and ui.R and load all packages we need. For now this will be C3 and shiny. The contents of this file will look like this:

library(shiny)
library(C3)

We can now remove the lines in server.R and ui.R where the shiny package is loaded.

If we remeber the layout of our dashboard application we see the gauges are layed out in 4 columns. We create this basic layout by using a fluidPage with a fluidRow and four columns

Our basic skeleton in ui.R will then look like this:

shinyUI(

  navbarPage(id="main", windowTitle = "Friss", position = "fixed-top", title = NULL,

             tabPanel("panel 1", icon = icon("dashboard"),

                      h3("Gauge demo"),

                      fluidRow(

                        column(3,
                               div( h4("Total", style = "text-align:center"),
                                    style = "width:75%; margin: auto")),

                        column(3,
                               div( h4("last 90 days", style = "text-align:center"),
                                    style = "width:75%; margin: auto")),

                        column(3,
                               div( h4("last 30 days", style = "text-align:center"),
                                    style = "width:75%; margin: auto")),

                        column(3,
                               div( h4("last 6 days", style = "text-align:center"),
                                    style = "width:75%; margin: auto"))
                      )

             )
  ))

Inside our columns we create div elements which contain a header for each gauge. Using the style properties of make sure our texts are centered and our div containers have some margin around them. If you run the app we created so far you can see the basic layout forming.

We are now ready to put the actual gauges in our app. When we created our widget the scaffoldWidget function automatically created C3GaugeOutput and renderC3Gauge functions to render our gauge widgets within a shiny application. For a detailed description how reactive render functions work see lesson 4 of the Rstudio shiny tutorial series.

We start by creating the render functions in server.R. To render a gauge we will generate a random number and use this as our value.

  output$Gauge1 <- renderC3Gauge({
    invalidateLater(5000, session)
    val <- round(runif(1,min=0,max=100),2)
    C3Gauge(message = val)
  })

The first line inside our render function makes a call to invalidateLater. This call causes the expression inside the render function to be called periodically in a similair fashion as the setInterval javascript function we used in the first tutorial. The first argument to invalidateLater is the period in milliseconds we want to use. The second argument is the shiny session object. The session object is required to cancel the scheduled invalidations after the shiny session has been closed. We want to create four gauges so we can copy the block above 3 times and simply name our ouput elements Gauge1, Gauge2, Gauge3 and Gauge4. The code inside the render functions remain the same.

All we have to do now is place C3GaugeOutput functions in our ui.R to display our rendered output. To do so we insert a call to C3GaugeOutput in each column like in the snippet below. We do this for Gauge1 till Gauge4.

  column(3,
         div( h4("Total", style = "text-align:center"),
              C3GaugeOutput("Gauge1", height = 150),
              style = "width:75%; margin: auto"))

The complete code for the resulting app can bee seen here and a live version of the app can be seen here.

As can be seen the gauges update rather abruptly. This is because in our first version of the gauge widget we do not handle updates properly yet. Whenever our C3Gauge function is called with each update a new gauge is created rather then the old one being updated. In general to be able to update the value or values of a widget we need to take the following steps:

  1. Check if we have an existing reference to the chart. 2 If not, create the chart and store a reference to it.
  2. If we have a reference update the existing chart.

Storing a reference and checking if it exists is the same for all the widgets. Updating data in a chart is specific for the javascript library we use. If we open C3Gauge.js in our C3 packge we created in our previous tutorial and take a look at our factory method we see the argument 'el' which is a reference to the element created by htmlWidgets where our chart will be put in.

factory: function(el, width, height)

We can use this element to store a reference to our chart once we created it with the folowing line

el.chart = chart;

To check if we have a reference to our chart we can do the following:

if(el.chart===undefined)

Putting this all toghether gives us the following pseudo code:

if(el.chart===undefined){
  var chart = c3.generate({...})
  el.chart = chart
}else{
  // Code to update chart
}

In our first tutorial we already updated the chart in this fiddle The part that updates the gauge is the call to the chart.load function

        chart.load({
          json: newData
        });

If we combine all the above to implement the outlined steps the renderValue function in C3Gauge.js becomes:

  renderValue: function(x) {

    // Check if we have a reference to our chart
    if(el.chart===undefined){
        // create a chart and set options
        // note that via the c3.js API we bind the chart to the element with id equal to chart1
        var chart = c3.generate({
            bindto: el,
            data: {
                json: x,
                type: 'gauge',
            },
            gauge: {
                label:{
                    //returning here the value and not the ratio
                    format: function(value, ratio){ return value;}
                },
                min: 0,
                max: 100,
                width: 15,
                units: 'value' //this is only the text for the label
            }
        });

        el.chart = chart;
    }else{
      // Update the chart if it already exists
      el.chart.load({json: x});
    }
}

When we reinstall and reload the package and run our app we can see the gauges updating smoothly as in the live version here



garrettgman/shiny-js-tutorials documentation built on May 16, 2019, 5:40 p.m.