В блестящем у меня есть две таблицы, отображаемые с помощью rhandsontable, я уже реализовал, что всякий раз, когда значение ячейки изменяется, ячейка окрашивается, однако при переключении между таблицами все изменения исчезают, поскольку, естественно, таблица перезагружается каждый раз с самого начала, возможно ли это чтобы как-то сохранить измененную ячейку, чтобы пока вы не вышли из сеанса или не нажали "сброс" все изменения сохранялись даже при переключении между таблицами?
(код основан на решении следующего треда: в блестящем: rhandsontable / afterChange, изменить сразу несколько фонов ячеек)
library(shiny)
library(rhandsontable)
library(tidyverse)
change_hook <- "function(el,x) {
hot = this.hot;
cellchngs = [];
afterChange = function(changes, source) {
$.each(changes, function (index, elem) {
change = elem; /* gather the row, col, old, new values */
if (change[2] !== change[3]) { /* if old isn't the same as new */
cellchg = ({rowind: change[0], colind: change[1]});
cellchngs.push(cellchg); /* add row and column indicies to array */
}
});
$.each(cellchngs, function(ind, elem) {
td = hot.getCell(elem['rowind'], elem['colind']); /* get the html element */
td.style.background = 'cyan'; /* set background color */
});
}
hot.addHook('afterChange', afterChange); /* add event to table */
}"
ui <- div(actionButton(inputId = "reset_button",label = "Reset"), selectInput("data", "Choose data",choices=c("mtcars"=1, "iris"=2), selected=1)
,rHandsontableOutput(outputId = "mtcars"))
server <- function(input, output, session) {
reset <- reactiveVal(0)
output$mtcars <- renderRHandsontable({
r = reset()
myvec <- c("mtcars", "iris")
mydata <- eval(parse(text=myvec[as.numeric(input$data)]))
rht = rhandsontable(mydata,reset=r,stretchH = "all",height=300)
reset(0)
htmlwidgets::onRender(rht,change_hook)
})
observeEvent(input$reset_button,
{
reset(1)
})
}
shinyApp(ui, server)
@StéphaneLaurent: кнопка сброса полностью сбросит все изменения в таблицах.
Я думаю, что это ошибка. Два экземпляра таблицы обмениваются данными.
@StéphaneLaurent: большое спасибо за изучение этой проблемы, так что вы не думаете, что тогда это решаемо?
Можно использовать два renderHansontableOutput.
Если у вас отображаются обе таблицы одновременно, вы можете сделать это с помощью нескольких изменений: 1) Вместо hot = this.hot; используйте var hot = el.htmlwidget_data_init_result.hot;. Префикс var к первому вхождению cellchngs, change, cellchg и td. (Например, вместо cellchngs = []; используйте var cellchngs = [];. Если это не имеет смысла или требует большей ясности, я могу добавить много деталей в качестве ответа. Однако это не отвечает на ваш фактический вопрос. Другой вариант — иметь обе таблицы «показаны» одновременно, но расположены друг над другом (поэтому вы видите только одну).
@Kat: спасибо, я тоже попробую этот подход



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Это немного некрасиво (с точки зрения избыточности кода)... но я устал, и это работает. В конце моего ответа я снова предоставил весь код, все вместе, для упрощения копирования и вставки.
Это извлекает выгоду из хранилища сеансов вашего браузера. Это означает, что пока это одно непрерывное событие, данные будут сохраняться. Если вы хотите иметь возможность закрыть браузер или перезапустить приложение и сохранить сохраненные изменения, вы можете вместо этого использовать локальное хранилище (sessionStorage вместо localStorage).
Это будет работать только в том случае, если у вас есть настроенные графики прямо сейчас. Если вы измените свой ui на две отдельные таблицы, это не сработает. (Я могу помочь, если вы настроите его по-другому, просто дайте мне знать.)
change_hook:wh захватит либо mtcars, либо iris из раскрывающегося меню selectInput.
cellchngs будет фиксировать изменения, как и раньше, но теперь у вас будет по одному для каждой таблицы. Есть три массива, потому что это был самый простой способ выровнять индексацию 1... в R и индексацию 0, 1.... в JS. Другими словами, этот первый массив в cellchngs останется пустым, следующий массив (индекс 1 в JS) будет фиксировать все изменения в selectInputvalue = 1 — это ваша mtcars таблица (как вы ее назначили).
afterChange не сильно изменился по сравнению с вашим первоначальным вопросом.
idx для захвата индекса из вашего selectInputcellchngs = на cellchngs[idx] = sCookie(): функция, которая сохраняет данные в хранилище сессий вашего браузера (см. изображение после этого списка изменений)chgs (это объявлено в вашем ui; подробнее об этом, когда я расскажу об изменениях в ui)if (sessionStorage... смотрит, есть ли данные, сохраненные в хранилище сеанса, если они есть, это обновит таблицу, чтобы отразить изменения, внесенные с момента запуска сеанса браузера (независимо от того, сколько раз вы переключаетесь между таблицами с помощью раскрывающегося списка)Глядя на хранилище сеансов: вы можете увидеть это в инструментах разработчика вашего браузера; вкладка: Приложение, левое меню: Хранилище сессий -> ваш IP (см. изображение ниже)
change_hook <- "function(el, x) {
var hot = el.htmlwidget_data_init_result.hot;
var wh = document.querySelector('#data option').innerHTML; /* DD table select */
var cellchngs = [[], [], []]; /* 3 arrays: 0 start index vs 1 start */
afterChange = function(changes, source) {
var idx = document.querySelector('#data option').value; /* DD table select */
$.each(changes, function (index, elem) {
var change = elem; /* gather the row, col, old, new values */
if (change[2] !== change[3]) { /* if old isn't the same as new */
var cellchg = ({rowind: change[0], colind: change[2]});
cellchngs[idx].push(cellchg); /* add row and column indicies to array */
sCookie(); /* save the updated data to cookie */
}
});
$.each(cellchngs[idx], function(ind, ele) {
var td = hot.getCell(ele['rowind'], ele['colind']); /* get the html element */
td.style.background = 'yellow'; /* set background color */
});
chgs[idx] = chgs[idx].concat(cellchngs[idx]); /* save list of changes to global var*/
chgs[idx].filter((v,i,a)=>a.findIndex(v2=>['rowind','colind'].every(k=>v2[k] ===v[k]))===i); /* remove duplicates */
}
hot.addHook('afterChange', afterChange); /* add event to table */
if (sessionStorage[wh]) { /* if data already stored for current table, retrieve it*/
hot.loadData(JSON.parse(sessionStorage[wh]));
hot.render();
colr(chgs, hot); /* re-highlight changes */
}
}"
uiЯ добавил элемент tags$script в ваш ui, код, который изначально был в вашем ui, остался прежним.
В этом сценарии вы найдете объявление глобальной переменной chgs, функцию colr: изменение цвета ячеек вне события изменения и sCookie: сохранение данных в хранилище сеанса.
ui <- div(
tags$script(HTML(
'setTimeout(function() { /* ensure table loads before looking for tbl elem */
chgs = [[], [], []]; /* global variable */
colr = function(chgs, hot) { /* for outside of change events (update data from stg */
var idx = document.querySelector("#data option").value;/* DD table select */
$.each(chgs[idx], function(ind, ele) {
var td = hot.getCell(ele["rowind"], ele["colind"]); /* get the html element */
td.style.background = "yellow"; /* set background color */
});
}
sCookie = function() { /* whenever data changes are made, save to local*/
var el = document.querySelector(".rhandsontable.html-widget"); /* capture table el */
var hot = el.htmlwidget_data_init_result.hot; /* capture instance */
var wh = document.querySelector("#data option").innerHTML; /* DD table select */
sessionStorage[wh] = JSON.stringify(hot.getData()); /* DD table select */
return
}
}, 200)')),
actionButton(inputId = "reset_button", label = "Reset"),
selectInput("data", "Choose data",
choices = c("mtcars" = 1, "iris" = 2), selected = 1),
rHandsontableOutput(outputId = "mtcars"))
Вот опять все, но все сразу. Если у вас есть какие-либо вопросы, дайте мне знать.
Обратите внимание, что я ничего не изменил в вашем server (хотя он может выглядеть иначе из-за пробелов).
library(shiny)
library(rhandsontable)
change_hook <- "function(el, x) {
var hot = el.htmlwidget_data_init_result.hot;
var wh = document.querySelector('#data option').innerHTML; /* DD table select */
var cellchngs = [[], [], []]; /* 3 arrays: 0 start index vs 1 start */
afterChange = function(changes, source) {
var idx = document.querySelector('#data option').value; /* DD table select */
$.each(changes, function (index, elem) {
var change = elem; /* gather the row, col, old, new values */
if (change[2] !== change[3]) { /* if old isn't the same as new */
var cellchg = ({rowind: change[0], colind: change[2]});
cellchngs[idx].push(cellchg); /* add row and column indicies to array */
sCookie(); /* save the updated data to cookie */
}
});
$.each(cellchngs[idx], function(ind, ele) {
var td = hot.getCell(ele['rowind'], ele['colind']); /* get the html element */
td.style.background = 'yellow'; /* set background color */
});
chgs[idx] = chgs[idx].concat(cellchngs[idx]); /* save list of changes to global var*/
chgs[idx].filter((v,i,a)=>a.findIndex(v2=>['rowind','colind'].every(k=>v2[k] ===v[k]))===i); /* remove duplicates */
}
hot.addHook('afterChange', afterChange); /* add event to table */
if (sessionStorage[wh]) { /* if data already stored for current table, retrieve it*/
hot.loadData(JSON.parse(sessionStorage[wh]));
hot.render();
colr(chgs, hot); /* re-highlight changes */
}
}"
ui <- div(
tags$script(HTML(
'setTimeout(function() { /* ensure table loads before looking for tbl elem */
chgs = [[], [], []]; /* global variable */
colr = function(chgs, hot) { /* for outside of change events (update data from stg */
var idx = document.querySelector("#data option").value;/* DD table select */
$.each(chgs[idx], function(ind, ele) {
var td = hot.getCell(ele["rowind"], ele["colind"]); /* get the html element */
td.style.background = "yellow"; /* set background color */
});
}
sCookie = function() { /* whenever data changes are made, save to local*/
var el = document.querySelector(".rhandsontable.html-widget"); /* capture table el */
var hot = el.htmlwidget_data_init_result.hot; /* capture instance */
var wh = document.querySelector("#data option").innerHTML; /* DD table select */
sessionStorage[wh] = JSON.stringify(hot.getData()); /* DD table select */
return
}
}, 200)')),
# tags$style(HTML(".colorMe {background: yellow !important;}")),
actionButton(inputId = "reset_button", label = "Reset"),
selectInput("data", "Choose data",
choices = c("mtcars" = 1, "iris" = 2), selected = 1),
rHandsontableOutput(outputId = "mtcars"))
server <- function(input, output, session) { # unchanged from your question
reset <- reactiveVal(0)
output$mtcars <- renderRHandsontable({
r = reset()
myvec <- c("mtcars", "iris")
mydata <- eval(parse(text = myvec[as.numeric(input$data)]))
rht = rhandsontable(mydata, reset = r, stretchH = "all", height = 300)
reset(0)
htmlwidgets::onRender(rht, change_hook)
})
observeEvent(input$reset_button, {reset(1)})
}
shinyApp(ui, server)
Большое спасибо, что нашли время и написали такой подробный ответ, однако, к сожалению, для меня это не работает ни в Rstudio, ни в браузере, когда я меняю значение, вокруг ячейки появляется только синий внешний прямоугольник, и все зависает . пробовал на файрфоксе и эдже
Для этого вы можете использовать скрытые tabsetPanels.
Обратите внимание, что я не изменил блок change_hook.
library(shiny)
library(rhandsontable)
change_hook <- "function(el,x) {
hot = this.hot;
cellchngs = [];
afterChange = function(changes, source) {
$.each(changes, function (index, elem) {
change = elem; /* gather the row, col, old, new values */
if (change[2] !== change[3]) { /* if old isn't the same as new */
cellchg = ({rowind: change[0], colind: change[1]});
cellchngs.push(cellchg); /* add row and column indicies to array */
}
});
$.each(cellchngs, function(ind, elem) {
td = hot.getCell(elem['rowind'], elem['colind']); /* get the html element */
td.style.background = 'cyan'; /* set background color */
});
}
hot.addHook('afterChange', afterChange); /* add event to table */
}"
ui <- fluidPage(
actionButton(inputId = "reset_button",label = "Reset"),
selectInput(
inputId = "data",
label = "Choose data",
choices = c("mtcars", "iris")
),
tabsetPanel(
id = "tabs",
type = "hidden",
tabPanelBody(
value = "mtcars",
rHandsontableOutput(outputId = "mtcars")
),
tabPanelBody(
value = "iris",
rHandsontableOutput(outputId = "iris")
)
)
)
server <- function(input, output, session) {
reset <- reactiveVal(0)
output$mtcars <- renderRHandsontable({
r = reset()
rht = rhandsontable(
mtcars,
reset = r,
stretchH = "all",
height = 300
)
reset(0)
htmlwidgets::onRender(rht, change_hook)
})
output$iris <- renderRHandsontable({
r = reset()
rht = rhandsontable(
iris,
reset = r,
stretchH = "all",
height = 300
)
reset(0)
htmlwidgets::onRender(rht, change_hook)
})
observeEvent(input$reset_button, {
reset(1)
})
# if the selectInput value changes, switch tabs:
observeEvent(input$data, {
updateTabsetPanel(
session = session,
inputId = "tabs",
selected = input$data
)
})
}
shinyApp(ui, server)
какова роль
reset?