Make several configuration saves/loadings of rpivottable on R

168 Views Asked by At

The following code I found on an other post (I could link if anybody is interested) adds the saving option on the R rpivottable using flexdashboard. The saving is done through cookies.
I am trying to recreate it but having multiple saving options, basically have SAVE1, SAVE2,.. and being able to load the wanted save. I am unexperienced with JS so it is really hard to do anything.

---
title: "rpivottable_test"
output: html_document
---
knitr::opts_chunk$set(echo = FALSE)
# devtools::install_github("fraupflaume/rpivotTable")
library(rpivotTable)
data(mtcars)
names(mtcars)[10] <- "George.Dontas"

Save Current Configuration Restore Previous Configuration


rpivotTable(mtcars,rows="George.Dontas", cols = c("cyl"), width = "90%", height = "40%",
            rendererOptions = list(
              c3 = list(legend = list(show = FALSE), 
                        data = list(labels = TRUE),
                        options = list(responsive = TRUE,
                                       maintainAspectRatio = FALSE),
                        size = list(width = "600",
                                    height = "500")),
              d3 = list(size = list(width = "500", height = "500")))) 

// save current state of the tables to my browser
setTimeout(function(){       //add the events first
  document.querySelector('a#saveBtn').addEventListener('click', savoring);
  document.querySelector('a#restoBtn').addEventListener('click', giveItBack);
  function savoring() {                             // function to save
    el = document.querySelectorAll('.rpivotTable');
    for(i=0; i < el.length; i++){
      elId = el[i].getAttribute("id");
      stringy = $('#' + elId).data("pivotUIOptions"); // collect rows/columns filters
      delete stringy['aggregators'];                 // remove the arbitrary
      delete stringy['renderers'];
      stringy2 = JSON.stringify(stringy);            // make it one key:value
      window.localStorage.setItem('table' + i, stringy2); // store it!
    }
  };
  function giveItBack() {                           // function to regurgitate
    el = document.querySelectorAll('.rpivotTable');
    console.log("working on the giver");
    ods = [...el[0].ownerDocument.scripts];         // make it an array
    for(j=0; j < el.length; j++){
      elId = el[j].getAttribute("id");
      where = ods.filter(function(ods){             // filter scripts for table data
        return ods.dataset['for'] === elId;
      })[0].innerHTML; 
      where2 = JSON.parse(where).x.data;            // WOOO HOO! I figured it out!!
      where3 = HTMLWidgets.dataframeToD3(where2);   // finally sheesh!!
      gimme = window.localStorage.getItem('table' + j); // get storage
      $('#' + elId).pivotUI(where3, JSON.parse(gimme), true, "en"); // put it back!
    }
  }
},100);

1

There are 1 best solutions below

10
Kat On BEST ANSWER

This uses the default system alert and prompt boxes. These vary in appearance a bit from one OS to another.

I've included CSS for the buttons and RMD. I used html_document. If you use flex_dashboard you won't need the CSS for the class main-container. The CSS for body may or may not be useful (depending on what else you have going on).

If you're using the Cran package, the rendererOptions won't work as-is. You have to remove a list level after you make the object. The D3 rendererOptions won't work unless you change the Javascript file, d3_renderers.js, in the rpivotTable package. (That's what I did with my fork.) If you use my fork, it will work as-is.

I've added handlers for

  • naming violations (whitespace, special characters, that sort of thing)
  • no matching name when restoring the configuration
    • returns a list of all names in storage
    • then prompts the user for another configuration name
  • if cancel is pressed from prompt, it cancels the save or restore process

You may notice quite a few changes in the restoration function. That's because the original code would lost subtotals, sorters, and the renderer tsv.

I tried to think of anything that could go wrong and this is where I'm at. If you run into any issues, let me know. If you have questions, ask.

The YAML, options, and CSS:

---
title: "button and input tester!"
author: "me"
date: '2022-05-12'
output: html_document
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE)
```

<style>
body {    /*push content away from far right and left edges*/
  margin-right: 2%;
  margin-left: 2%;
}
.main-container {
  max-width: unset;
}
.btn { 
  vertical-align: middle;
  -moz-box-shadow: 0px 10px 14px -7px #000000;
  -webkit-box-shadow: 0px 10px 14px -7px #000000;
  box-shadow: 0px 10px 14px -7px #000000;
  -moz-border-radius: 4px;
  -webkit-border-radius: 4px;
  border-radius: 4px;
  border: .5px solid black;
  display: inline-block;
  font-size: 1.2em;           
  padding: .3em 0px;
  width: 20em;                 
  text-decoration: none; /*no underline!!*/
  cursor: pointer;
}
.btn:active {      /*simulate movement*/
  position: relative;
  top: 1px;
}
</style>

The table.

```{r data,include=F}
# devtools::install_github("fraupflaume/rpivotTable")
library(rpivotTable)
data(mtcars)

```

## Make it Interesting...or not

Do you want to save or restore the previously saved pivot tables' configuration?

<a id='saveNamed' class='btn' style="background-color:#003b70;color:white;">Save Configuration by Name</a>
<a id='restoNamed' class='btn' style="background-color:#b21e29;color:white;">Restore Configuration with Custom Name</a>


```{r showMe, echo=FALSE, fig.show="hold"}
rpivotTable(mtcars, rows="am", cols = c("cyl"), width = "90%", 
            height = "40%", subtotals = TRUE,
            rendererOptions = list(
              c3 = list(legend = list(show = FALSE), 
                        data = list(labels = TRUE),
                        options = list(responsive = TRUE,
                                       maintainAspectRatio = FALSE),
                        size = list(width = "600",
                                    height = "500")),
              d3 = list(size = list(width = "500", height = "500")) 
            ))
```


That's all, folks.

Lastly, the JS.

```{r listenOrElse,results="asis",engine="js"}

// for ONE TABLE
setTimeout(function(){  // add to buttons
  document.querySelector('a#saveNamed').addEventListener('click', savoring); 
  document.querySelector('a#restoNamed').addEventListener('click', giveItBack);
  function savoring() {                              // function to save
    el = document.querySelector('.rpivotTable');
    msg = "Choose a name for the configuration that you are saving.";
    inName = prompt(msg, ['Enter a name with no spaces or special characters'])
    if(inName === null) {return;};                   // they changed their mind; nothing saved
    inName = inName.replace(/[^a-z0-9.]/gi, '');     // validate string
    path = window.location.pathname.split("/").pop().split(".").slice()[0]; //filename
    elId = el.getAttribute("id");
    stringy = $('#' + elId).data("pivotUIOptions");  // collect rows/col filters
    delete stringy['aggregators'];                   // remove not-parse-friendly keys
    delete stringy['renderers'];
    stringy2 = JSON.stringify(stringy);              // one key:value pair for storage
    window.localStorage.setItem(path + '_' + inName, stringy2);  // STORE it!
  };
  function giveItBack() {                           // function to regurgitate
    el = document.querySelector('.rpivotTable');
    msg = "Enter the name of the configuration you would like to retrieve.";
    confName = prompt(msg, ["Enter a name with no spaces or special characters"]);
    if(confName === null) {return;}; 
    confName = confName.replace(/[^a-z0-9.]/gi, '');    // validate string
    ods = [...el.ownerDocument.scripts];             // make it an array
    path = window.location.pathname.split("/").pop().split(".").slice()[0]; //filename
    elId = el.getAttribute("id");
    where = ods.filter(function(ods){             // filter scripts for data
      return ods.dataset['for'] === elId;
    })[0].innerHTML; 
    where2 = JSON.parse(where).x.data;            // format data for pivotUI()
    where3 = HTMLWidgets.dataframeToD3(where2);   // ...still formatting
    if(window.localStorage.getItem(path + '_' + confName) === null) { // alert
      len = window.localStorage.length
      var str;
      for(i = 0; i < len; i++) {
        w = window.localStorage.key(i);
        ind = w.lastIndexOf('_');
        w2 = w.substr(ind + 1);   // remove file/page name and table number
        str = str + w2 + '\n';    // make one long string of names
      }
      str2 = "WARNING: There is no saved pivot table configuration with the name " + confName + '.';
      str2 += " Here is a list of the configuration names that are currently stored for this page:\n";
      str2 += str;
      alert(str2);
      confName = prompt(msg, ["Enter a name with no spaces or special characters"]);
      if(confName === null) {
        return
      }; 
      confName = confName.replace(/[^a-z0-9.]/gi, '');    // validate string
    }
    gimme = window.localStorage.getItem(path + '_' + confName); // get storage
    gimmeMore = JSON.parse(gimme);                            // prepare for recall
    if(where.includes('"subtotals":true')){       // is the option 'subtotal' used?
      gimmeMore.renderers = $.pivotUtilities.subtotal_renderers;
      gimmeMore.dataClass = $.pivotUtilities.SubtotalPivotData;
    }; 
    if(where.includes('"tsv":true')){             // is the option 'tsv' used?
      gimmeMore.renderers = $.extend(gimmeMore.renderers, $.pivotUtilities.export_renderers);
    };
    if(where.includes('sortAs')){
      // passed as a function, they will get lost in save & retrieve
      stringy = $('#' + elId).data("pivotUIOptions").sorters;
      gimmeMore.sorters = stringy;
    }
    $('#' + elId).pivotUI(where3, gimmeMore, true, "en"); // put it back!
  };
}, 500);

```

In this image, you can see that 'bar chart' was entered (name violation).

enter image description here

During restoration, I searched for 'bar' (which didn't exist).

enter image description here

enter image description here

enter image description here

Now when I'm prompted again, I'm going to type bar chart (with the space) even though you can see that 'barchart' exists, not bar chart.

enter image description here

You'll still get the chart. The handler will remove white space and special characters here, as well.

enter image description here