Save/Load rpivottable 配置
Save/Load rpivottable configuration
我在几个 (rmarkdown) 网页上使用 rpivottable。
我已经看到 an example here 的 saving/restoring table 配置 to/from cookie。
由于我不擅长 javascript,我想问一下是否可以在 rmd
页面中以编程方式在 table 控件之上添加两个按钮,从而允许用户save/load 他们的首选 table 配置(cookie 或本地文件,如果可能)。您能否提供实现该目的的示例代码?
谢谢。
这个花了点时间。我使用本地存储。我这里有很多样式,但是没必要。我使用了 flexdashboard
的输出,因为这往往会给我带来最多的 JS 问题。
<style>
body { /*push content away from far right and left edges*/
margin-right: 2%;
margin-left: 2%;
}
.rpivotTable {
overflow:auto;
resize: both;
box-shadow: 0 22px 70px 4px rgba(0,0,0,0.56);
-moz-box-shadow: 0 22px 70px 4px rgba(0,0,0,0.56);
-webkit-box-shadow: 0 22px 70px 4px rgba(0,0,0,0.56);
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
border: 1px solid white;
padding: 5px;
margin: 5px 20px 50px 5px;
}
.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.3em;
padding: .3em 0px;
width: 18em;
text-decoration: none; /*no underline!!*/
cursor: pointer;
}
.btn:active { /*simulate movement*/
position: relative;
top: 1px;
}
</style>
我已经使用了我在其他问题中找到的内容。
## R Markdown
<div style="margin-right:5%;">
`r stringi::stri_rand_lipsum(10)`
</div>
```{r cars}
library(rpivotTable)
data(mtcars)
names(mtcars)[10] <- "George.Dontas"
```
Here is the **first** Div.
## Including Plots
Do you want to save or restore the previously saved pivot tables' configuration?
<a id='saveBtn' class='btn' style="background-color:#003b70;color:white;">Save Current Configuration</a>
<a id='restoBtn' class='btn' style="background-color:#b21e29;color:white;">Restore Previous Configuration</a>
```{r pressure, echo=FALSE, fig.show="hold"}
rpivotTable(mtcars,rows="George.Dontas", cols=c("cyl","carb"),width="100%", height="400px")
```
```{r morePressure, echo=FALSE, fig.show="hold"}
rpivotTable(mtcars,rows="George.Dontas", cols=c("cyl","carb"),width="100%", height="400px")
```
This should be a different aspect of the report.
```{r evenMorePressure, echo=FALSE, fig.show="hold"}
rpivotTable(mtcars,rows="George.Dontas", cols=c("cyl","carb"),width="100%", height="400px")
```
这是JS/JQuery...它有点难看,而且是两者的大杂烩(JS/JQuery)。
```{r listenOrElse,results="as-is",engine="js"}
// 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);
```
更新
感谢@George Dontas 指出一些改进的机会。此更新更改了配置的保存方式。不过,我相信仍然有改进的方法。
此更新将文件或网页名称添加为用于存储信息的 key-value 对的一部分。现在,webpage/script 和 table 号码的名称都需要匹配才能更新 table。此外,这将在无法恢复配置时提醒用户。如果没有保存任何内容并且没有保存文件名和 table 匹配配置,则会出现此警报。
保存配置的更新
savoring()
新增一行代码,修改一行代码。
新:
path = window.location.pathname.split("/").pop().split(".").slice()[0]; //f name
修改:
window.localStorage.setItem(path + '_table' + i, stringy2); // store it
整个函数有变化:
function savoring() { // function to save
el = document.querySelectorAll('.rpivotTable');
path = window.location.pathname.split("/").pop().split(".").slice()[0];
for(i=0; i < el.length; i++){
elId = el[i].getAttribute("id");
stringy = $('#' + elId).data("pivotUIOptions"); // collect filters
delete stringy['aggregators']; // remove the arbitrary
delete stringy['renderers'];
stringy2 = JSON.stringify(stringy); // make it one key:value
window.localStorage.setItem(path + '_table' + i, stringy2); // store it
}
};
恢复配置的更新
此函数中的新行很少。名称必须收集,如 savoring()
更改。此外,此功能现在为用户提供了警报。
I started out with the basic system alert, but it wasn't up to snuff for my tastes, so I also developed a custom alert box. I've included both here.
基本警报和更新配置检索
与我最初的基本警报答案相比,唯一不同的是 giveItBack()
函数中的以下代码行:
path = window.location.pathname.split("/").pop().split(".").slice()[0]; //f name
和
if(window.localStorage.getItem(path + '_table' + j) === null) {
jj = j + 1;
alert("WARNING: There is no saved pivot table configuration for " + path + "'s table " + jj + ".");
continue; // don't update, go to next table (if more than 1)
}
这里是完整的giveItBack()
函数(注意这里有notice(msg)
和msg
,但是注释掉了):
function giveItBack() { // function to regurgitate
el = document.querySelectorAll('.rpivotTable');
console.log("working on the giver");
ods = [...el[0].ownerDocument.scripts]; // make it an array
path = window.location.pathname.split("/").pop().split(".").slice()[0]; //name
for(j=0; j < el.length; j++){
elId = el[j].getAttribute("id");
where = ods.filter(function(ods){ // filter scripts 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 formatted
// is there a saved configuration that matches this file and table?
if(window.localStorage.getItem(path + '_table' + j) === null) {
jj = j + 1;
//this is for the standard alert box
alert("WARNING: There is no saved pivot table configuration for " + path + "'s table " + jj + ".");
//msg = "<b>WARNING</b><br><br>There is no saved pivot table configuration for<br>" + path + "."
//notice(msg); //this is for the custom alert box
continue; // go to next loop
}
gimme = window.localStorage.getItem(path + '_table' + j); // get storage
$('#' + elId).pivotUI(where3, JSON.parse(gimme), true, "en"); // put it back!
}
};
自定义警报和更新的配置检索
如果您选择对警报消息使用更自定义的方法,则还有很多(幸运的是,它应该是复制和粘贴)。您将使用基本警报更新中的 giveItBack
函数,但注释掉或删除 alert(...
并取消注释 msg
和 notice()
.
For the CSS in my original answer, update the styles for .btn
to .btn, #noted
and .btn:active
to btn:active, #noted:active
.
这是自定义提醒的剩余 CSS。您可以将此 CSS 添加到其他样式标签或将它们分开。
<style>
#notice-wrapper {
width: 100%;
position: fixed;
top: 0;
left: 0;
z-index: 1000000;
background: transparent;
display: none;
transition: opacity 1s ease-in;
}
#notice-box {
-moz-box-shadow: 0px 10px 14px -7px #000000;
-webkit-box-shadow: 0px 10px 14px -7px #000000;
box-shadow: 0px 10px 14px -7px #000000;
border-radius: 4px;
border: .5px solid black;
width = 300px;
background: #003b70;
color: white;
min-height: 200px;
position: absolute;
top: 50%;
left: 50%;
margin: -100px 0 0 -150px;
}
#notHead {
text-align: center;
font-size: 1.3em;
padding: 4px;
margin: 2.5em;
font-family: Verdana, sans-serif;
}
#noted {
background: #b21e29;
margin: .5em;
width: 120px;
font-family: Verdana, sans-serif;
}
</style>
接下来是自定义警告框的 JS。我把这个函数放在 setTimeout(function(){
和 savoring()
和 giveItBack()
中。
function notice(msg) {
function cr() {
if(document.querySelector('#notice-wrapper') === null) {
wrapper = document.createElement('div');
wrapper.id = 'notice-wrapper';
html = "<div id='notice-box'><h2 id='notHead'></h2><div id='noticeBtns'>";
html += "<button id='noted'>OK</button></div></div>";
wrapper.innerHTML = html;
document.body.appendChild(wrapper);
}
insta = document.querySelector('#notice-wrapper');
placer(insta);
return(insta);
}
function placer(insta) {
wrapper = insta;
winHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientheight;
wrapper.style.height = winHeight + "px";
}
function showy(el) {
el.style.display = "block";
el.style.opacity = 1;
}
function goAway(el) {
el.style.opacity = 0;
setTimeout(function(){
el.style.display = "none";
}, 1000);
}
function takeAction(msg) {
insta = cr();
insta.querySelector('#notHead').innerHTML = msg;
showy(insta);
insta.querySelector('#noted').addEventListener('click', function() {
goAway(insta);
}, false);
}
takeAction(msg);
}
当然,使用此自定义选项,您可以根据自己的喜好设置样式。样式控制不是系统警报消息系统的选项。
不要标记这个答案——它不是另一个答案——它是一个更新。 (并且写给任何可能阅读它的人;而不仅仅是问...的人)
这种保存和恢复table配置的更新方法是基于包rpivotTable
的修改版本(在Github中:fraupflaume/rpivotTable
)。
I've kept this answer self-contained so that you would not have to look at the other answer to assemble the code.
- 这适用于同一 RMarkdown 呈现网页中的一对多
rpivotTable
小部件。
- Dynamic Sizing: 此脚本包含动态调整大小,受网页可用宽度限制(不会与其他内容重叠,不会 使网页变宽)。为了动态调整大小——
- 如果使用输出
html_document
,则必须取消设置 class main-container
的 max-width。此设置会产生巨大的边距,对于此小部件来说 anti-conducive。
- 您需要使用
overflow: auto
设置 class .rpivotTable
的样式;您不能将 resize
分配给此 class。
- 如果使用输出
flex_dashboard
,则不能保留默认设置 vertical-scroll: fit
(与动态调整大小相反!)
- 您不能在小部件级别设置小部件的默认高度和宽度。设置此项会使大小 完全 静态。
- 完全可以接受table在创建table(
rpivotTable()
)时将table的高度和宽度设置为静态或动态尺寸。
- 我忘记了什么?
- Cookie-ish Alert:配置保存有两个选项用于呈现警报(表示没有数据要恢复的警报)。
- 系统警报:如果使用系统警报,cookie 函数中有两行Javascript。与警报相关的 Javascript 和 CSS 的其余部分用于自定义警报选项。
- Custom Alert:我将从两个角度来讨论这个问题:CSS 和 JS。
- CSS:唯一需要 CSS 确保 pop-up 警报确认按钮具有
cursor: pointer
样式。我 相信 超出此范围的样式是严格的偏好。与警报相关的 CSS 包括以下 classes 和 id:#noted
、#notice-wrapper
、notice-box
和 notHead
。
- JS:cookie-ish函数中只有几行JS,
giveItBack
。警告框的 notice
函数。
- Cookie-ish 与
subtotal
:此问题的修复(以及 tsv
可能存在的问题)需要cookie-ish 函数 giveItBack
中的几行额外代码。 savoring
没变。
- Cookie-ish对齐方式:目前配置是通过文件名或网页名和迭代来标识的(当你有多个table,它们按索引出现的顺序)。考虑重新排序 table 或重命名 file/page.
的影响
动态大小块
这个块可以放在你脚本的任何地方;假设:echo=FALSE
是默认值,或者输出选项隐藏回声。
```{r spicy, engine="js", include=FALSE, results="asis"}
scrp = ["https://cdnjs.cloudflare.com/ajax/libs/css-element-queries/1.2.3/ElementQueries.min.js",
"https://cdnjs.cloudflare.com/ajax/libs/css-element-queries/1.2.3/ResizeSensor.js"];
setTimeout(function(){ // this function adds the URLs to the HTML <head>
for(i=0; i < scrp.length; i++) {
script = document.createElement('script');
script.src = scrp[i];
script.type = "text/javascript";
document.head.appendChild(script);
}
ElementQueries.listen(); // just listen!!
}, 200); //wait a sec!
```
Cookie-ish 和警报
的 JS
应该有很多评论来解读其作用的基本思想。
```{r listenOrElse,results="asis",engine="js"}
// save current state of the tables to my browser
setTimeout(function(){
// add to buttons
document.querySelector('a#saveBtn').addEventListener('click', savoring);
document.querySelector('a#restoBtn').addEventListener('click', giveItBack);
function savoring() { // function to save
el = document.querySelectorAll('.rpivotTable');
path = window.location.pathname.split("/").pop().split(".").slice()[0]; //filename
for(i=0; i < el.length; i++){
elId = el[i].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 + '_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
path = window.location.pathname.split("/").pop().split(".").slice()[0]; //filename
for(j=0; j < el.length; j++){
elId = el[j].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 + '_table' + j) === null) { // alert
// basic system alert
//jj = j + 1;
//alert("WARNING: There is no saved pivot table configuration for " + path + "'s table " + jj + ".");
// custom alert
msg = "<b>WARNING</b><br><br>There is no saved pivot table configuration for<br>" + path + "."
notice(msg);
// Either Alert: from here on needed whether basic or custom alert
continue;
}
gimme = window.localStorage.getItem(path + '_table' + j); // get storage
gimmeMore = JSON.parse(gimme); // prepare for recall
if(where.includes('"subtotals":true')){ // is the option 'subtotals' 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);
};
$('#' + elId).pivotUI(where3, gimmeMore, true, "en"); // put it back!
}
};
function notice(msg) { // all of this is for the custom alert box
function cr() {
if(document.querySelector('#notice-wrapper') === null) { // if an alert doesn't exist
wrapper = document.createElement('div');
wrapper.id = 'notice-wrapper';
html = "<div id='notice-box'><h2 id='notHead'></h2><div id='noticeBtns'>";
html += "<button id='noted'>OK</button></div></div>";
wrapper.innerHTML = html;
document.body.appendChild(wrapper);
}
insta = document.querySelector('#notice-wrapper'); // a container for the alert box
placer(insta);
return(insta);
}
function placer(insta) { // make the size reasonable, based on viewing screen
wrapper = insta;
winHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientheight;
wrapper.style.height = winHeight + "px";
}
function showy(el) {
el.style.display = "block"; // keep the content confined; add a control
el.style.opacity = 1;
}
function goAway(el) { // use the control to hide the alert when ack
el.style.opacity = 0;
setTimeout(function(){
el.style.display = "none";
}, 1000);
}
function takeAction(msg) { // use all of the above: make alert and render it
insta = cr();
insta.querySelector('#notHead').innerHTML = msg;
showy(insta);
insta.querySelector('#noted').addEventListener('click', function() {
goAway(insta);
}, false);
}
takeAction(msg); // pop-up ENGAGED
}
},200); // give me a sec—my browser may be slow... or my widgets may be fast... or...
```
CSS
这不会进入 R 块。这是我示例中使用的所有 CSS。我没有使用外部样式 sheet,尽管这是更好的做法。
此 CSS 控制警报、用于保存和检索配置的按钮,以及正文和边距样式。我包含了 CSS 以使枢轴 tables 弹出,但它被注释掉了(这些是 /* CSS commenters */
)。您可以use/not自行决定使用。
<style>
body { /*push content away from far right and left edges*/
margin-right: 2%;
margin-left: 2%;
}
.main-container {
max-width: unset; // remove default from RMD
}
.rpivotTable {
overflow:auto; /*this is absolutely needed*/
/*resize: both; <- cannot have this*/
/*box-shadow: 0 22px 70px 4px rgba(0,0,0,0.56);
-moz-box-shadow: 0 22px 70px 4px rgba(0,0,0,0.56);
-webkit-box-shadow: 0 22px 70px 4px rgba(0,0,0,0.56);
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
border: 1px solid white;
padding: 5px;
margin: 5px 20px 50px 5px;
max-width: 1100px;*/
}
.btn, #noted {
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.3em;
padding: .3em 0px;
width: 18em;
text-decoration: none; /*no underline!!*/
cursor: pointer;
}
.btn:active, #noted:active { /*simulate movement*/
position: relative;
top: 1px;
}
#notice-wrapper {
width: 100%;
position: fixed;
top: 0;
left: 0;
z-index: 1000000;
background: transparent;
display: none;
transition: opacity 1s ease-in;
}
#notice-box {
-moz-box-shadow: 0px 10px 14px -7px #000000;
-webkit-box-shadow: 0px 10px 14px -7px #000000;
box-shadow: 0px 10px 14px -7px #000000;
border-radius: 4px;
border: .5px solid black;
width = 300px;
background: #003b70;
color: white;
min-height: 200px;
position: absolute;
top: 50%;
left: 50%;
margin: -100px 0 0 -150px;
}
#notHead {
text-align: center;
font-size: 1.3em;
padding: 4px;
margin: 2.5em;
font-family: Verdana, sans-serif;
}
#noted {
background: #b21e29;
margin: .5em;
width: 120px;
font-family: Verdana, sans-serif;
}
</style>
R <- 那也是...嗯,剩下的RMD...
这是我用作示例的 RMD 的代码。我唯一没有包括的是我的版本说明(现在是 16...)。
---
title: "rpivottable_test"
output: html_document
---
```{r setup,include=F}
knitr::opts_chunk$set(echo = FALSE)
```
```{r data,include=F}
# devtools::install_github("fraupflaume/rpivotTable")
library(rpivotTable)
data(mtcars)
names(mtcars)[10] <- "George.Dontas"
```
## Make it Interesting...or not
Do you want to save or restore the previously saved pivot tables' configuration?
<!--- cookie-ish's buttons --->
<a id='saveBtn' class='btn' style="background-color:#003b70;color:white;">Save Current Configuration</a>
<a id='restoBtn' class='btn' style="background-color:#b21e29;color:white;">Restore Previous Configuration</a>
```{r showMe, echo=FALSE, fig.show="hold"}
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"))))
```
`r stringi::stri_rand_lipsum(3)`
## How about Another Table?
Tell me things. Make sure I am not going to overlap later. You better be listening!
```{r morePressure, echo=FALSE, fig.show="hold"}
rp <- rpivotTable(mtcars, rows = c("mpg", "am"), cols = "cyl",
width = "90%", height = "40%", subtotals = T,
tsv = T,
rendererOptions = list(
c3 = list(legend = list(show = FALSE), # this works!!
data = list(labels = TRUE),
size = list(width = "600",
height = "500"),
options = list(responsive = TRUE,
maintainAspectRatio = FALSE))))
rp
```
This should be *anywhere* other than here.
```{r itsMine, echo=FALSE, fig.show="hold"}
df1 <- data.frame(where = LETTERS[1:3], what = c(3.6, 5.6, 1.1))
x = rpivotTable(df1, width="80%", height="40%",
aggregatorName = "Count",
vals = "Sum",
cols = "where",
rows = "what",
rendererOptions = list(c3 = list(legend = list(show = FALSE), # this works!!
data = list(labels = TRUE),
size = list(width = "500",
height = "500"),
options = list(responsive = TRUE,
maintainAspectRatio = FALSE))))
x
```
Put something here
第一个table我只控制了d3
大小。下图反映了控制和不受控制的树图大小调整之间的差异。
我在浏览器中进行了缩小,以便可以看到第二个树状图的限制:
虽然我试图提供足够的信息让任何遇到此问答的人都可以使用它,但我还是焦急地等待提问者找到破解它的方法:)
我在几个 (rmarkdown) 网页上使用 rpivottable。
我已经看到 an example here 的 saving/restoring table 配置 to/from cookie。
由于我不擅长 javascript,我想问一下是否可以在 rmd
页面中以编程方式在 table 控件之上添加两个按钮,从而允许用户save/load 他们的首选 table 配置(cookie 或本地文件,如果可能)。您能否提供实现该目的的示例代码?
谢谢。
这个花了点时间。我使用本地存储。我这里有很多样式,但是没必要。我使用了 flexdashboard
的输出,因为这往往会给我带来最多的 JS 问题。
<style>
body { /*push content away from far right and left edges*/
margin-right: 2%;
margin-left: 2%;
}
.rpivotTable {
overflow:auto;
resize: both;
box-shadow: 0 22px 70px 4px rgba(0,0,0,0.56);
-moz-box-shadow: 0 22px 70px 4px rgba(0,0,0,0.56);
-webkit-box-shadow: 0 22px 70px 4px rgba(0,0,0,0.56);
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
border: 1px solid white;
padding: 5px;
margin: 5px 20px 50px 5px;
}
.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.3em;
padding: .3em 0px;
width: 18em;
text-decoration: none; /*no underline!!*/
cursor: pointer;
}
.btn:active { /*simulate movement*/
position: relative;
top: 1px;
}
</style>
我已经使用了我在其他问题中找到的内容。
## R Markdown
<div style="margin-right:5%;">
`r stringi::stri_rand_lipsum(10)`
</div>
```{r cars}
library(rpivotTable)
data(mtcars)
names(mtcars)[10] <- "George.Dontas"
```
Here is the **first** Div.
## Including Plots
Do you want to save or restore the previously saved pivot tables' configuration?
<a id='saveBtn' class='btn' style="background-color:#003b70;color:white;">Save Current Configuration</a>
<a id='restoBtn' class='btn' style="background-color:#b21e29;color:white;">Restore Previous Configuration</a>
```{r pressure, echo=FALSE, fig.show="hold"}
rpivotTable(mtcars,rows="George.Dontas", cols=c("cyl","carb"),width="100%", height="400px")
```
```{r morePressure, echo=FALSE, fig.show="hold"}
rpivotTable(mtcars,rows="George.Dontas", cols=c("cyl","carb"),width="100%", height="400px")
```
This should be a different aspect of the report.
```{r evenMorePressure, echo=FALSE, fig.show="hold"}
rpivotTable(mtcars,rows="George.Dontas", cols=c("cyl","carb"),width="100%", height="400px")
```
这是JS/JQuery...它有点难看,而且是两者的大杂烩(JS/JQuery)。
```{r listenOrElse,results="as-is",engine="js"}
// 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);
```
更新
感谢@George Dontas 指出一些改进的机会。此更新更改了配置的保存方式。不过,我相信仍然有改进的方法。
此更新将文件或网页名称添加为用于存储信息的 key-value 对的一部分。现在,webpage/script 和 table 号码的名称都需要匹配才能更新 table。此外,这将在无法恢复配置时提醒用户。如果没有保存任何内容并且没有保存文件名和 table 匹配配置,则会出现此警报。
保存配置的更新
savoring()
新增一行代码,修改一行代码。
新:
path = window.location.pathname.split("/").pop().split(".").slice()[0]; //f name
修改:
window.localStorage.setItem(path + '_table' + i, stringy2); // store it
整个函数有变化:
function savoring() { // function to save
el = document.querySelectorAll('.rpivotTable');
path = window.location.pathname.split("/").pop().split(".").slice()[0];
for(i=0; i < el.length; i++){
elId = el[i].getAttribute("id");
stringy = $('#' + elId).data("pivotUIOptions"); // collect filters
delete stringy['aggregators']; // remove the arbitrary
delete stringy['renderers'];
stringy2 = JSON.stringify(stringy); // make it one key:value
window.localStorage.setItem(path + '_table' + i, stringy2); // store it
}
};
恢复配置的更新
此函数中的新行很少。名称必须收集,如 savoring()
更改。此外,此功能现在为用户提供了警报。
I started out with the basic system alert, but it wasn't up to snuff for my tastes, so I also developed a custom alert box. I've included both here.
基本警报和更新配置检索
与我最初的基本警报答案相比,唯一不同的是 giveItBack()
函数中的以下代码行:
path = window.location.pathname.split("/").pop().split(".").slice()[0]; //f name
和
if(window.localStorage.getItem(path + '_table' + j) === null) {
jj = j + 1;
alert("WARNING: There is no saved pivot table configuration for " + path + "'s table " + jj + ".");
continue; // don't update, go to next table (if more than 1)
}
这里是完整的giveItBack()
函数(注意这里有notice(msg)
和msg
,但是注释掉了):
function giveItBack() { // function to regurgitate
el = document.querySelectorAll('.rpivotTable');
console.log("working on the giver");
ods = [...el[0].ownerDocument.scripts]; // make it an array
path = window.location.pathname.split("/").pop().split(".").slice()[0]; //name
for(j=0; j < el.length; j++){
elId = el[j].getAttribute("id");
where = ods.filter(function(ods){ // filter scripts 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 formatted
// is there a saved configuration that matches this file and table?
if(window.localStorage.getItem(path + '_table' + j) === null) {
jj = j + 1;
//this is for the standard alert box
alert("WARNING: There is no saved pivot table configuration for " + path + "'s table " + jj + ".");
//msg = "<b>WARNING</b><br><br>There is no saved pivot table configuration for<br>" + path + "."
//notice(msg); //this is for the custom alert box
continue; // go to next loop
}
gimme = window.localStorage.getItem(path + '_table' + j); // get storage
$('#' + elId).pivotUI(where3, JSON.parse(gimme), true, "en"); // put it back!
}
};
自定义警报和更新的配置检索
如果您选择对警报消息使用更自定义的方法,则还有很多(幸运的是,它应该是复制和粘贴)。您将使用基本警报更新中的 giveItBack
函数,但注释掉或删除 alert(...
并取消注释 msg
和 notice()
.
For the CSS in my original answer, update the styles for
.btn
to.btn, #noted
and.btn:active
tobtn:active, #noted:active
.
这是自定义提醒的剩余 CSS。您可以将此 CSS 添加到其他样式标签或将它们分开。
<style>
#notice-wrapper {
width: 100%;
position: fixed;
top: 0;
left: 0;
z-index: 1000000;
background: transparent;
display: none;
transition: opacity 1s ease-in;
}
#notice-box {
-moz-box-shadow: 0px 10px 14px -7px #000000;
-webkit-box-shadow: 0px 10px 14px -7px #000000;
box-shadow: 0px 10px 14px -7px #000000;
border-radius: 4px;
border: .5px solid black;
width = 300px;
background: #003b70;
color: white;
min-height: 200px;
position: absolute;
top: 50%;
left: 50%;
margin: -100px 0 0 -150px;
}
#notHead {
text-align: center;
font-size: 1.3em;
padding: 4px;
margin: 2.5em;
font-family: Verdana, sans-serif;
}
#noted {
background: #b21e29;
margin: .5em;
width: 120px;
font-family: Verdana, sans-serif;
}
</style>
接下来是自定义警告框的 JS。我把这个函数放在 setTimeout(function(){
和 savoring()
和 giveItBack()
中。
function notice(msg) {
function cr() {
if(document.querySelector('#notice-wrapper') === null) {
wrapper = document.createElement('div');
wrapper.id = 'notice-wrapper';
html = "<div id='notice-box'><h2 id='notHead'></h2><div id='noticeBtns'>";
html += "<button id='noted'>OK</button></div></div>";
wrapper.innerHTML = html;
document.body.appendChild(wrapper);
}
insta = document.querySelector('#notice-wrapper');
placer(insta);
return(insta);
}
function placer(insta) {
wrapper = insta;
winHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientheight;
wrapper.style.height = winHeight + "px";
}
function showy(el) {
el.style.display = "block";
el.style.opacity = 1;
}
function goAway(el) {
el.style.opacity = 0;
setTimeout(function(){
el.style.display = "none";
}, 1000);
}
function takeAction(msg) {
insta = cr();
insta.querySelector('#notHead').innerHTML = msg;
showy(insta);
insta.querySelector('#noted').addEventListener('click', function() {
goAway(insta);
}, false);
}
takeAction(msg);
}
当然,使用此自定义选项,您可以根据自己的喜好设置样式。样式控制不是系统警报消息系统的选项。
不要标记这个答案——它不是另一个答案——它是一个更新。 (并且写给任何可能阅读它的人;而不仅仅是问...的人)
这种保存和恢复table配置的更新方法是基于包rpivotTable
的修改版本(在Github中:fraupflaume/rpivotTable
)。
I've kept this answer self-contained so that you would not have to look at the other answer to assemble the code.
- 这适用于同一 RMarkdown 呈现网页中的一对多
rpivotTable
小部件。 - Dynamic Sizing: 此脚本包含动态调整大小,受网页可用宽度限制(不会与其他内容重叠,不会 使网页变宽)。为了动态调整大小——
- 如果使用输出
html_document
,则必须取消设置 classmain-container
的 max-width。此设置会产生巨大的边距,对于此小部件来说 anti-conducive。 - 您需要使用
overflow: auto
设置 class.rpivotTable
的样式;您不能将resize
分配给此 class。 - 如果使用输出
flex_dashboard
,则不能保留默认设置vertical-scroll: fit
(与动态调整大小相反!) - 您不能在小部件级别设置小部件的默认高度和宽度。设置此项会使大小 完全 静态。
- 完全可以接受table在创建table(
rpivotTable()
)时将table的高度和宽度设置为静态或动态尺寸。 - 我忘记了什么?
- 如果使用输出
- Cookie-ish Alert:配置保存有两个选项用于呈现警报(表示没有数据要恢复的警报)。
- 系统警报:如果使用系统警报,cookie 函数中有两行Javascript。与警报相关的 Javascript 和 CSS 的其余部分用于自定义警报选项。
- Custom Alert:我将从两个角度来讨论这个问题:CSS 和 JS。
- CSS:唯一需要 CSS 确保 pop-up 警报确认按钮具有
cursor: pointer
样式。我 相信 超出此范围的样式是严格的偏好。与警报相关的 CSS 包括以下 classes 和 id:#noted
、#notice-wrapper
、notice-box
和notHead
。 - JS:cookie-ish函数中只有几行JS,
giveItBack
。警告框的notice
函数。
- CSS:唯一需要 CSS 确保 pop-up 警报确认按钮具有
- Cookie-ish 与
subtotal
:此问题的修复(以及tsv
可能存在的问题)需要cookie-ish 函数giveItBack
中的几行额外代码。savoring
没变。 - Cookie-ish对齐方式:目前配置是通过文件名或网页名和迭代来标识的(当你有多个table,它们按索引出现的顺序)。考虑重新排序 table 或重命名 file/page. 的影响
动态大小块
这个块可以放在你脚本的任何地方;假设:echo=FALSE
是默认值,或者输出选项隐藏回声。
```{r spicy, engine="js", include=FALSE, results="asis"}
scrp = ["https://cdnjs.cloudflare.com/ajax/libs/css-element-queries/1.2.3/ElementQueries.min.js",
"https://cdnjs.cloudflare.com/ajax/libs/css-element-queries/1.2.3/ResizeSensor.js"];
setTimeout(function(){ // this function adds the URLs to the HTML <head>
for(i=0; i < scrp.length; i++) {
script = document.createElement('script');
script.src = scrp[i];
script.type = "text/javascript";
document.head.appendChild(script);
}
ElementQueries.listen(); // just listen!!
}, 200); //wait a sec!
```
Cookie-ish 和警报
的 JS应该有很多评论来解读其作用的基本思想。
```{r listenOrElse,results="asis",engine="js"}
// save current state of the tables to my browser
setTimeout(function(){
// add to buttons
document.querySelector('a#saveBtn').addEventListener('click', savoring);
document.querySelector('a#restoBtn').addEventListener('click', giveItBack);
function savoring() { // function to save
el = document.querySelectorAll('.rpivotTable');
path = window.location.pathname.split("/").pop().split(".").slice()[0]; //filename
for(i=0; i < el.length; i++){
elId = el[i].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 + '_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
path = window.location.pathname.split("/").pop().split(".").slice()[0]; //filename
for(j=0; j < el.length; j++){
elId = el[j].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 + '_table' + j) === null) { // alert
// basic system alert
//jj = j + 1;
//alert("WARNING: There is no saved pivot table configuration for " + path + "'s table " + jj + ".");
// custom alert
msg = "<b>WARNING</b><br><br>There is no saved pivot table configuration for<br>" + path + "."
notice(msg);
// Either Alert: from here on needed whether basic or custom alert
continue;
}
gimme = window.localStorage.getItem(path + '_table' + j); // get storage
gimmeMore = JSON.parse(gimme); // prepare for recall
if(where.includes('"subtotals":true')){ // is the option 'subtotals' 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);
};
$('#' + elId).pivotUI(where3, gimmeMore, true, "en"); // put it back!
}
};
function notice(msg) { // all of this is for the custom alert box
function cr() {
if(document.querySelector('#notice-wrapper') === null) { // if an alert doesn't exist
wrapper = document.createElement('div');
wrapper.id = 'notice-wrapper';
html = "<div id='notice-box'><h2 id='notHead'></h2><div id='noticeBtns'>";
html += "<button id='noted'>OK</button></div></div>";
wrapper.innerHTML = html;
document.body.appendChild(wrapper);
}
insta = document.querySelector('#notice-wrapper'); // a container for the alert box
placer(insta);
return(insta);
}
function placer(insta) { // make the size reasonable, based on viewing screen
wrapper = insta;
winHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientheight;
wrapper.style.height = winHeight + "px";
}
function showy(el) {
el.style.display = "block"; // keep the content confined; add a control
el.style.opacity = 1;
}
function goAway(el) { // use the control to hide the alert when ack
el.style.opacity = 0;
setTimeout(function(){
el.style.display = "none";
}, 1000);
}
function takeAction(msg) { // use all of the above: make alert and render it
insta = cr();
insta.querySelector('#notHead').innerHTML = msg;
showy(insta);
insta.querySelector('#noted').addEventListener('click', function() {
goAway(insta);
}, false);
}
takeAction(msg); // pop-up ENGAGED
}
},200); // give me a sec—my browser may be slow... or my widgets may be fast... or...
```
CSS
这不会进入 R 块。这是我示例中使用的所有 CSS。我没有使用外部样式 sheet,尽管这是更好的做法。
此 CSS 控制警报、用于保存和检索配置的按钮,以及正文和边距样式。我包含了 CSS 以使枢轴 tables 弹出,但它被注释掉了(这些是 /* CSS commenters */
)。您可以use/not自行决定使用。
<style>
body { /*push content away from far right and left edges*/
margin-right: 2%;
margin-left: 2%;
}
.main-container {
max-width: unset; // remove default from RMD
}
.rpivotTable {
overflow:auto; /*this is absolutely needed*/
/*resize: both; <- cannot have this*/
/*box-shadow: 0 22px 70px 4px rgba(0,0,0,0.56);
-moz-box-shadow: 0 22px 70px 4px rgba(0,0,0,0.56);
-webkit-box-shadow: 0 22px 70px 4px rgba(0,0,0,0.56);
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
border: 1px solid white;
padding: 5px;
margin: 5px 20px 50px 5px;
max-width: 1100px;*/
}
.btn, #noted {
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.3em;
padding: .3em 0px;
width: 18em;
text-decoration: none; /*no underline!!*/
cursor: pointer;
}
.btn:active, #noted:active { /*simulate movement*/
position: relative;
top: 1px;
}
#notice-wrapper {
width: 100%;
position: fixed;
top: 0;
left: 0;
z-index: 1000000;
background: transparent;
display: none;
transition: opacity 1s ease-in;
}
#notice-box {
-moz-box-shadow: 0px 10px 14px -7px #000000;
-webkit-box-shadow: 0px 10px 14px -7px #000000;
box-shadow: 0px 10px 14px -7px #000000;
border-radius: 4px;
border: .5px solid black;
width = 300px;
background: #003b70;
color: white;
min-height: 200px;
position: absolute;
top: 50%;
left: 50%;
margin: -100px 0 0 -150px;
}
#notHead {
text-align: center;
font-size: 1.3em;
padding: 4px;
margin: 2.5em;
font-family: Verdana, sans-serif;
}
#noted {
background: #b21e29;
margin: .5em;
width: 120px;
font-family: Verdana, sans-serif;
}
</style>
R <- 那也是...嗯,剩下的RMD...
这是我用作示例的 RMD 的代码。我唯一没有包括的是我的版本说明(现在是 16...)。
---
title: "rpivottable_test"
output: html_document
---
```{r setup,include=F}
knitr::opts_chunk$set(echo = FALSE)
```
```{r data,include=F}
# devtools::install_github("fraupflaume/rpivotTable")
library(rpivotTable)
data(mtcars)
names(mtcars)[10] <- "George.Dontas"
```
## Make it Interesting...or not
Do you want to save or restore the previously saved pivot tables' configuration?
<!--- cookie-ish's buttons --->
<a id='saveBtn' class='btn' style="background-color:#003b70;color:white;">Save Current Configuration</a>
<a id='restoBtn' class='btn' style="background-color:#b21e29;color:white;">Restore Previous Configuration</a>
```{r showMe, echo=FALSE, fig.show="hold"}
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"))))
```
`r stringi::stri_rand_lipsum(3)`
## How about Another Table?
Tell me things. Make sure I am not going to overlap later. You better be listening!
```{r morePressure, echo=FALSE, fig.show="hold"}
rp <- rpivotTable(mtcars, rows = c("mpg", "am"), cols = "cyl",
width = "90%", height = "40%", subtotals = T,
tsv = T,
rendererOptions = list(
c3 = list(legend = list(show = FALSE), # this works!!
data = list(labels = TRUE),
size = list(width = "600",
height = "500"),
options = list(responsive = TRUE,
maintainAspectRatio = FALSE))))
rp
```
This should be *anywhere* other than here.
```{r itsMine, echo=FALSE, fig.show="hold"}
df1 <- data.frame(where = LETTERS[1:3], what = c(3.6, 5.6, 1.1))
x = rpivotTable(df1, width="80%", height="40%",
aggregatorName = "Count",
vals = "Sum",
cols = "where",
rows = "what",
rendererOptions = list(c3 = list(legend = list(show = FALSE), # this works!!
data = list(labels = TRUE),
size = list(width = "500",
height = "500"),
options = list(responsive = TRUE,
maintainAspectRatio = FALSE))))
x
```
Put something here
第一个table我只控制了d3
大小。下图反映了控制和不受控制的树图大小调整之间的差异。
我在浏览器中进行了缩小,以便可以看到第二个树状图的限制:
虽然我试图提供足够的信息让任何遇到此问答的人都可以使用它,但我还是焦急地等待提问者找到破解它的方法:)