在客户端使用 afterChange 事件更改 rHandsontable 的单元格背景
Change cell background of rHandsontable with afterChange event on client side
我想在客户端用户编辑可操作单元格后更改它的背景颜色。 handsontable 是通过 Shiny 应用程序定义的;所以这实际上是一个关于如何在 Shiny 应用程序的 rHandsontable 中定义事件挂钩的问题。我试图完成的一般用例是:用户编辑单元格数据;背景颜色的变化表明它已被更改并且正在等待保存到数据库中;更改被传回 Shiny 的 observeEvent()
;更改被发送到外部数据库并保存; rHandsontable 在输出上重新绘制,默认背景着色删除颜色集是更改。结果是闪烁表示数据已保存。如果出现数据库连接错误或其他问题,颜色将持续存在,表明数据未保存。我已经能够完成一个工作示例,粘贴在下面。
具体问题:钩子目前是使用 hot_col(1,renderer=change_hook)
实现的,但是,这与渲染单元格无关,只是一种允许添加钩子的方法。我假设 hot_table()
是正确的函数,但它可以用来注册事件吗?更一般地说,是否有更内置的方法来完成此操作?
change_hook <- "
function(instance, td, row, col, prop, value, cellProperties) {
Handsontable.hooks.add('afterChange', function(changes,source) {
if (source === 'edit' || source === 'undo' || source === 'autofill' || source === 'paste') {
row = changes[0][0];
col = changes[0][1];
oldval = changes[0][2];
newval = changes[0][3];
if (oldval !== newval) {
cell = this.getCell(row,col);
cell.style.background = 'pink';
}
}
},instance);
Handsontable.renderers.TextRenderer.apply(this, arguments);
}"
ui <- div(width=300,rHandsontableOutput(outputId="hTable"))
server <- function(input, output, session) {
df<-data.frame(col1=c("Hands","on","Table"),col2=c(100,200,300),stringsAsFactors = F)
hTable <- reactiveVal(df)
observeEvent(input$hTable, {
withProgress(message = "Saving changes to database...", value=0.5, {
Sys.sleep(1)
incProgress(1, detail = "done")
input_hTable <- hot_to_r(input$hTable)
hTable(input_hTable)
})
})
output$hTable <- renderRHandsontable({
rhandsontable(hTable(),stretchH="all",height=300) %>%
hot_col(1,renderer=change_hook)
})
}
shinyApp(ui, server)
经过一段时间的搜索,我使用了这些页面 (handsontable, htmlwidgets) 中的信息来更改已编辑单元格的背景颜色。
在 afterChange 函数中,将保留所有已更改单元格的列表。在 afterRender 函数中,这些单元格的背景颜色将更改为黄色。在afterLoadData函数中,改变单元格的背景色会重置为白色,并清空。
library(shiny)
library(rhandsontable)
change_hook <- "function(el,x) {
var hot = this.hot;
var cellChanges = [];
var changefn = function(changes,source) {
if (source === 'edit' || source === 'undo' || source === 'autofill' || source === 'paste') {
row = changes[0][0];
col = changes[0][1];
oldval = changes[0][2];
newval = changes[0][3];
if (oldval !== newval) {
var cell = hot.getCell(row, col);
cell.style.background = 'pink';
cellChanges.push({'rowid':row, 'colid':col});
}
}
}
var renderfn = function(isForced) {
for(i = 0; i < cellChanges.length; i++)
{
var rowIndex = cellChanges[i]['rowid'];
var columnIndex = cellChanges[i]['colid'];
var cell = hot.getCell(rowIndex, columnIndex);
cell.style.background = 'yellow';
}
}
var loadfn = function(initialLoad) {
for(i = 0; i < cellChanges.length; i++)
{
var rowIndex = cellChanges[i]['rowid'];
var columnIndex = cellChanges[i]['colid'];
var cell = hot.getCell(rowIndex, columnIndex);
cell.style.background = 'white';
}
cellChanges = []
}
hot.addHook('afterChange', changefn);
hot.addHook('afterRender', renderfn);
hot.addHook('afterLoadData', loadfn);
} "
ui <- div(actionButton(inputId = "reset_button",label = "Reset")
,rHandsontableOutput(outputId="hTable"))
server <- function(input, output, session) {
df<-data.frame(col1=c("Hands","on","Table"),col2=c(100,200,300),stringsAsFactors = F)
reset <- reactiveVal(0)
hTable <- reactiveVal(df)
output$hTable <- renderRHandsontable({
r = reset()
rht = rhandsontable(hTable(),reset=r,stretchH="all",height=300)%>%
hot_col('col1',readOnly=T)
reset(0)
htmlwidgets::onRender(rht,change_hook)
})
observeEvent(input$reset_button,
{
reset(1)
})
}
shinyApp(ui, server)
下面的代码在用户编辑后更改 rhandsontable 单元格的背景颜色。
library(shiny)
library(rhandsontable)
ui <- fluidPage(
titlePanel ('Give it a try and make changes!'),
mainPanel(rHandsontableOutput('table'))
)
server <- function(input, output, session) {
df <- data.frame(SYMBOLS = c('AAPL', 'ALRM', 'AMZN', 'BABA', 'CRM', 'CSCO',
'FB'),
NAMES = c('APPLE', 'ALARM.com', 'AMAZON', 'ALIBABA',
'SALESFORCE', 'CISCO', 'FACEBOOK'),
NOTE = c('sale', '', 'buy', '', '', '', 'watch'))
output$table <- renderRHandsontable(
rhandsontable(df))
rv <- reactiveValues(row = c(), col = c())
observeEvent(input$table$changes$changes, {
rv$row <- c(rv$row, input$table$changes$changes[[1]][[1]])
rv$col <- c(rv$col, input$table$changes$changes[[1]][[2]])
output$table <- renderRHandsontable({
rhandsontable(hot_to_r(input$table), row_highlight = rv$row,
col_highlight = rv$col) %>%
hot_cols(
renderer = "
function(instance, td, row, col, prop, value, cellProperties) {
Handsontable.renderers.TextRenderer.apply(this, arguments);
if (instance.params) {
hrows = instance.params.row_highlight;
hrows = hrows instanceof Array ? hrows : [hrows];
hcols = instance.params.col_highlight;
hcols = hcols instanceof Array ? hcols : [hcols];
}
for (let i = 0; i < hrows.length; i++) {
if (instance.params && hrows[i] == row && hcols[i] == col) {
td.style.background = 'pink';
}}
}"
)
})
})
}
shinyApp(ui, server)
每改变一个单元格,
- 此单元格的行索引 (
input$table$changes$changes[[1]][[1]]
) 及其列索引 (input$table$changes$changes[[1]][[2]]
) 分别附加到反应变量 rv$row
和 rv$col
,
- rHandsontable 使用反应变量 (
rv
) 再次呈现,其中包括到目前为止已更改的所有单元格的索引,最后,
- javascript 渲染器中的
for
循环针对 table 的所有列和行一次一对地遍历 rv
中的所有索引。如果行和列索引都匹配,则此单元格的背景颜色更改为粉红色。
我想在客户端用户编辑可操作单元格后更改它的背景颜色。 handsontable 是通过 Shiny 应用程序定义的;所以这实际上是一个关于如何在 Shiny 应用程序的 rHandsontable 中定义事件挂钩的问题。我试图完成的一般用例是:用户编辑单元格数据;背景颜色的变化表明它已被更改并且正在等待保存到数据库中;更改被传回 Shiny 的 observeEvent()
;更改被发送到外部数据库并保存; rHandsontable 在输出上重新绘制,默认背景着色删除颜色集是更改。结果是闪烁表示数据已保存。如果出现数据库连接错误或其他问题,颜色将持续存在,表明数据未保存。我已经能够完成一个工作示例,粘贴在下面。
具体问题:钩子目前是使用 hot_col(1,renderer=change_hook)
实现的,但是,这与渲染单元格无关,只是一种允许添加钩子的方法。我假设 hot_table()
是正确的函数,但它可以用来注册事件吗?更一般地说,是否有更内置的方法来完成此操作?
change_hook <- "
function(instance, td, row, col, prop, value, cellProperties) {
Handsontable.hooks.add('afterChange', function(changes,source) {
if (source === 'edit' || source === 'undo' || source === 'autofill' || source === 'paste') {
row = changes[0][0];
col = changes[0][1];
oldval = changes[0][2];
newval = changes[0][3];
if (oldval !== newval) {
cell = this.getCell(row,col);
cell.style.background = 'pink';
}
}
},instance);
Handsontable.renderers.TextRenderer.apply(this, arguments);
}"
ui <- div(width=300,rHandsontableOutput(outputId="hTable"))
server <- function(input, output, session) {
df<-data.frame(col1=c("Hands","on","Table"),col2=c(100,200,300),stringsAsFactors = F)
hTable <- reactiveVal(df)
observeEvent(input$hTable, {
withProgress(message = "Saving changes to database...", value=0.5, {
Sys.sleep(1)
incProgress(1, detail = "done")
input_hTable <- hot_to_r(input$hTable)
hTable(input_hTable)
})
})
output$hTable <- renderRHandsontable({
rhandsontable(hTable(),stretchH="all",height=300) %>%
hot_col(1,renderer=change_hook)
})
}
shinyApp(ui, server)
经过一段时间的搜索,我使用了这些页面 (handsontable, htmlwidgets) 中的信息来更改已编辑单元格的背景颜色。
在 afterChange 函数中,将保留所有已更改单元格的列表。在 afterRender 函数中,这些单元格的背景颜色将更改为黄色。在afterLoadData函数中,改变单元格的背景色会重置为白色,并清空。
library(shiny)
library(rhandsontable)
change_hook <- "function(el,x) {
var hot = this.hot;
var cellChanges = [];
var changefn = function(changes,source) {
if (source === 'edit' || source === 'undo' || source === 'autofill' || source === 'paste') {
row = changes[0][0];
col = changes[0][1];
oldval = changes[0][2];
newval = changes[0][3];
if (oldval !== newval) {
var cell = hot.getCell(row, col);
cell.style.background = 'pink';
cellChanges.push({'rowid':row, 'colid':col});
}
}
}
var renderfn = function(isForced) {
for(i = 0; i < cellChanges.length; i++)
{
var rowIndex = cellChanges[i]['rowid'];
var columnIndex = cellChanges[i]['colid'];
var cell = hot.getCell(rowIndex, columnIndex);
cell.style.background = 'yellow';
}
}
var loadfn = function(initialLoad) {
for(i = 0; i < cellChanges.length; i++)
{
var rowIndex = cellChanges[i]['rowid'];
var columnIndex = cellChanges[i]['colid'];
var cell = hot.getCell(rowIndex, columnIndex);
cell.style.background = 'white';
}
cellChanges = []
}
hot.addHook('afterChange', changefn);
hot.addHook('afterRender', renderfn);
hot.addHook('afterLoadData', loadfn);
} "
ui <- div(actionButton(inputId = "reset_button",label = "Reset")
,rHandsontableOutput(outputId="hTable"))
server <- function(input, output, session) {
df<-data.frame(col1=c("Hands","on","Table"),col2=c(100,200,300),stringsAsFactors = F)
reset <- reactiveVal(0)
hTable <- reactiveVal(df)
output$hTable <- renderRHandsontable({
r = reset()
rht = rhandsontable(hTable(),reset=r,stretchH="all",height=300)%>%
hot_col('col1',readOnly=T)
reset(0)
htmlwidgets::onRender(rht,change_hook)
})
observeEvent(input$reset_button,
{
reset(1)
})
}
shinyApp(ui, server)
下面的代码在用户编辑后更改 rhandsontable 单元格的背景颜色。
library(shiny)
library(rhandsontable)
ui <- fluidPage(
titlePanel ('Give it a try and make changes!'),
mainPanel(rHandsontableOutput('table'))
)
server <- function(input, output, session) {
df <- data.frame(SYMBOLS = c('AAPL', 'ALRM', 'AMZN', 'BABA', 'CRM', 'CSCO',
'FB'),
NAMES = c('APPLE', 'ALARM.com', 'AMAZON', 'ALIBABA',
'SALESFORCE', 'CISCO', 'FACEBOOK'),
NOTE = c('sale', '', 'buy', '', '', '', 'watch'))
output$table <- renderRHandsontable(
rhandsontable(df))
rv <- reactiveValues(row = c(), col = c())
observeEvent(input$table$changes$changes, {
rv$row <- c(rv$row, input$table$changes$changes[[1]][[1]])
rv$col <- c(rv$col, input$table$changes$changes[[1]][[2]])
output$table <- renderRHandsontable({
rhandsontable(hot_to_r(input$table), row_highlight = rv$row,
col_highlight = rv$col) %>%
hot_cols(
renderer = "
function(instance, td, row, col, prop, value, cellProperties) {
Handsontable.renderers.TextRenderer.apply(this, arguments);
if (instance.params) {
hrows = instance.params.row_highlight;
hrows = hrows instanceof Array ? hrows : [hrows];
hcols = instance.params.col_highlight;
hcols = hcols instanceof Array ? hcols : [hcols];
}
for (let i = 0; i < hrows.length; i++) {
if (instance.params && hrows[i] == row && hcols[i] == col) {
td.style.background = 'pink';
}}
}"
)
})
})
}
shinyApp(ui, server)
每改变一个单元格,
- 此单元格的行索引 (
input$table$changes$changes[[1]][[1]]
) 及其列索引 (input$table$changes$changes[[1]][[2]]
) 分别附加到反应变量rv$row
和rv$col
, - rHandsontable 使用反应变量 (
rv
) 再次呈现,其中包括到目前为止已更改的所有单元格的索引,最后, - javascript 渲染器中的
for
循环针对 table 的所有列和行一次一对地遍历rv
中的所有索引。如果行和列索引都匹配,则此单元格的背景颜色更改为粉红色。