javascriptjqueryrshinyrpivottable

Shiny + JS: Conditional Formatting based on Pivot Values


I am using the rpivotTable htmlwidget, which wraps the excellent PivotTable.js library. I want to conditionally format the pivot table based on the values of the cells.

To do this, I have tried to adapt the function here. Here is a minimal Shiny app with rpivotTable:

rm(list = ls())
library(shiny)
library(shinydashboard)
library(rpivotTable)
library(dplyr)

#==========================================================
# simulate some data for the pivot table
#==========================================================
df_pivot = data_frame(
  factor1 = sample(rep(LETTERS[1:2], 100)),
  factor2 = sample(rep(LETTERS[5:6], 100)),
  factor3 = sample(rep(LETTERS[19:20], 100)),
  value = abs(rnorm(200))
)

#==========================================================
# ui
#==========================================================
pivot_body = dashboardBody({
  tags$head(includeScript("pivot.js"))
  tags$head(
    tags$style(
      HTML(
        ".realGone { background-color: #F08080 !important; }"
      )
    )
  )
  rpivotTableOutput(outputId = "pivot_output")
})

pivot_header = dashboardHeader(title = "Some title.")
pivot_sidebar = dashboardSidebar()

pivot_ui = dashboardPage(
  header = pivot_header,
  sidebar = pivot_sidebar,
  body = pivot_body
)

#==========================================================
# server
#==========================================================
pivot_server = shinyServer(function(input, output, session) {
  output$pivot_output = renderRpivotTable({
    rpivotTable(
      data = df_pivot,
      rows = "factor1",
      cols = "factor2"
    )
  })
})

#==========================================================
# run the app
#==========================================================
pivot_app = shinyApp(
  ui = pivot_ui,
  server = pivot_server
)

runApp(pivot_app)

And here is my adaptation of the JS function -- the basic idea is to look for elements with the class .pvtVal, add a class to them and apply CSS styling based on this class.

$(document).ready(function(){
var $labels = $('.pvtVal');
console.log("Reached here.");
  for (var i=0; i<$labels.length; i++) {
    if ($labels[i].innerHTML < 12) {
            $('.pvtVal').eq(i).addClass('expired');
        } else if ($labels[i].innerHTML > 12 && $labels[i].innerHTML < 14) {
          $('.pvtVal').eq(i).addClass('dead');
      } else if ($labels[i].innerHTML > 14) {
        $('.pvtVal').eq(i).addClass('realGone');
      }
  }
});

But when I inspect the elements in the console, they do not appear to have the realGone class added. My guess is that I have misunderstood what $document().ready does.


Solution

  • There are several issues with your code.

    1. dashboardBody should be a function with several arguments instead of a list of code.

    Correct: dashboardBody(item1, item2, item3)

    Wrong: dashboardBody({line1, line2, line3})

    1. The .pvtVal table td cell is created by pivotTable.js, so it is essential that your own Javascript runs after pivotTable.js completes. Unfortunately, this occurs after the document.ready or window.load event. I used the technique from Running jQuery after all other JS has executed to continuously poll the page and see if the table cell appears.

    Complete working code

    app.R

    rm(list = ls())
    library(shiny)
    library(shinydashboard)
    library(rpivotTable)
    library(dplyr)
    
    #==========================================================
    # simulate some data for the pivot table
    #==========================================================
    df_pivot = data_frame(
        factor1 = sample(rep(LETTERS[1:2], 100)),
        factor2 = sample(rep(LETTERS[5:6], 100)),
        factor3 = sample(rep(LETTERS[19:20], 100)),
        value = abs(rnorm(200))
    )
    
    #==========================================================
    # ui
    #==========================================================
    pivot_body = dashboardBody(
        tags$head(
            tags$style(
                HTML(
                    ".realGone { background-color: #F08080 !important; }"
                )
            )
        ),
        rpivotTableOutput(outputId = "pivot_output"),
        tags$script(src="pivot.js")
    
    )
    
    pivot_header = dashboardHeader(title = "Some title.")
    pivot_sidebar = dashboardSidebar()
    
    pivot_ui = dashboardPage(
        header = pivot_header,
        sidebar = pivot_sidebar,
        body = pivot_body
    )
    
    #==========================================================
    # server
    #==========================================================
    pivot_server = shinyServer(function(input, output, session) {
        output$pivot_output = renderRpivotTable({
            rpivotTable(
                data = df_pivot,
                rows = "factor1",
                cols = "factor2"
            )
        })
    })
    
    #==========================================================
    # run the app
    #==========================================================
    
    shinyApp(ui = pivot_ui, server = pivot_server)
    

    pivot.js (make sure to put this in the www folder which should be a subfolder of the project root)

    $(window).load(function(){
        var i = setInterval(function() {
            if ($(".pvtVal").length) {
                clearInterval(i);
    
                $(".pvtVal").each(function(index) {
    
                    var value = parseInt($(this).text());
    
                    if (value < 12) {
                        $(this).addClass("expired");
                    } else if (value > 12 && value < 14) {
                        $(this).addClass("dead");
                    } else {
                        $(this).addClass("realGone");
                    }
                });
            }
        }, 100);
    });