R语言 闪亮/可处理/更新单元格

wqlqzqxt  于 2023-06-19  发布在  其他
关注(0)|答案(2)|浏览(77)

在shine中,我有两个表显示rhandsontable,我已经实现了,每当单元格值被更改时,单元格会被着色,但是当在表之间切换时,所有的更改都会消失,因为自然表每次从一开始重新加载,是否有可能以某种方式保存更改的单元格?所以只要你不退出会话或不按“重置”所有的更改保留,即使你在表之间切换?
(code基于以下线程的解决方案:in shiny: rhandsontable / afterChange, change multiple cell backgrounds at once

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)
dly7yett

dly7yett1#

您可以使用隐藏的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)

pjngdqdw

pjngdqdw2#

这是一个有点丑陋(在编码冗余方面)...但我累了,它的工作。在我的回答的最后,我再次提供了所有的代码,所有在一起,更容易复制+粘贴。

这利用了浏览器的 * 会话存储 *。这意味着只要是一个连续的事件,数据就会保存。如果您希望能够关闭浏览器或重新运行应用程序,并且仍然保留保存的更改,则可以使用本地存储(sessionStoragelocalStorage)。
这将只适用于您现在已设置的图表。如果您将ui更改为具有两个单独的表,则这将无法工作。(如果你设置得不一样,我可以帮你,只要让我知道就行了。)

change_hook的修改:

wh将从selectInput下拉菜单中捕获mtcarsiris
cellchngs将像以前一样捕获更改,但现在每个表都有一个。有三个数组,因为它是最简单的方式来对齐1...在R中索引和0,1…JS中的索引换句话说,cellchngs中的第一个数组将保持为空,下一个数组(JS中的索引1)将捕获selectInputvalue = 1中的所有更改-这就是您的mtcars表(正如您所分配的那样)。
afterChange与您最初的问题没有太大变化。

  • 添加了idx以捕获selectInput中的索引
  • cellchngs =修改为cellchngs[idx] =
  • 添加sCookie():将数据保存在浏览器的会话存储中的函数(请参阅此更改列表后的图像)
  • 使用chgs添加了全局var集合(这是在您的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

我已经在你的ui中添加了一个tags$script元素,你原来在ui中的代码保持不变。
在此脚本中,您将找到全局变量chgs的声明,即函数colr:在change事件之外更改单元格颜色,以及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)

相关问题