r - 在 Shiny 的应用程序中搜索多个 rhandsontable 的功能

标签 r shiny rhandsontable

我有一个带有多个选项卡的 Shiny 应用程序,我在其中渲染rhandsontable并希望提供搜索功能。这是我编写的用于使用搜索呈现此类表格的模块:

# Module for rendering rhandsontable with search
rtable_UI <- function(id) {
  ns <- NS(id)
  tagList(
    textInput("searchField", "Search"),
    rhandsontable::rHandsontableOutput(ns('table_output'))
  )
}

rtableServer <- function(id, df) {
  moduleServer(id, function(input, output, session) {
    output$table_output <- rhandsontable::renderRHandsontable({
      rhandsontable::rhandsontable(df, search = TRUE)
    })
  })
}

请注意,searchField 不在其命名空间中。如果我尝试 ns("searchField") 搜索功能在我 Shiny 的应用程序中不起作用:

# Shiny App
ui <- navbarPage(
  "example",
  tabPanel(
    'First Tab',
    rtable_UI('table1')
  ),
  tabPanel(
    'Second Tab',
    rtable_UI('table2')
  )
)

server <- function(input, output, session){
  rtableServer('table1', iris)
  rtableServer('table2', mtcars)
}

shinyApp(ui, server)

搜索功能仅适用于第一个选项卡,我认为这是因为搜索字段的 id 是相同的。然而,更改 id 似乎也不是一个选项,如 here 所示。 。有没有办法让rhandsontable知道命名空间?

最佳答案

这是一个相当痛苦的练习! RHandsontable 真的很想负责搜索……我离题了。

如果您添加了更多表,您唯一需要添加的是我编写的位置 tbr[1].classList.add('active');/* 因此数据可用 */.加载时仅加载第一个选项卡。您需要加载所有选项卡才能使代码正常工作。这只需发生一次,因此,例如,如果有三个表,则需要添加 tbr[2].classList.add('active');

I didn't use any string safeguards (setting it to lowercase, trimming whitespace, etc.).

我在这里将搜索输入框的 id 更改为 searcher,这样 rhandsontable 包就无法识别它。这必须与 JS 中使用的 id 相匹配,因此如果您在此处更改此字符串,它也必须在那里更改。

rtable_UI <- function(id) {
  ns <- NS(id)
  tagList(
    textInput(ns("searcher"), "Search"),
    rhandsontable::rHandsontableOutput(ns('table_output'))
  )
}

这没有改变。

rtableServer <- function(id, df) {
  moduleServer(id, function(input, output, session) {
    output$table_output <- rhandsontable::renderRHandsontable({
      rhandsontable::rhandsontable(df, search = T)
    })
  })
} 

对于 ui,我添加了 tagListtags$head、一个样式标签和两个脚本。一个脚本是 Shiny 渲染所需的更改;另一个脚本是搜索功能。这里编码的一些内容肯定可以使用 shinyhtmltools 之间的 tag 函数来完成,但这对我来说更容易一些。

ui <- tagList(
  tags$head(
    tags$style(HTML(
      ".htSearchResult {background-color: rgb(252, 237, 217);}")),
    tags$script(type="text/javascript", HTML("setTimeout(function(){
      var tbr = document.querySelectorAll('.tab-pane');
      tbr[1].classList.add('active'); /* so the data is avail */
      var inStr = document.querySelectorAll('input');        /*the input boxes*/
      var widg = document.querySelectorAll('.html-widget');  /*the tables boxes*/
      for(i = 0; i < widg.length; i++) {
        var wId = widg[i].getAttribute('id');                /* collect table IDs */
        inStr[i].className = '';             /* disassociate rhandstable search */
        inStr[i].style.cssText = 'cursor: pointer; font-size: 1em; line-height: 1.5em; padding: 6px 12px; display: block; width: 70%;';
          /* collect label and input box; add event without rhandsontable interference */
        var par = inStr[i].parentElement    
        var ipar = par.children[0].outerHTML;
        var str = inStr[i].outerHTML;
        var html = '\"replacer(\\'' + wId + '\\')\"';
        str = str.replace('>', ' onkeyup=' + html + '>');
        par.innerHTML = ipar + str;
      }
      }, 100)")),
      tags$script(type="text/javascript", HTML("function replacer(tbl) {
        $('#' + tbl).first().find('.htSearchResult').removeClass('htSearchResult'); /*remove previous search highlight*/
        var searchword = $('#' + tbl.substring(0,6) + '-searcher').val();       /* collect input */
        var custfilter = new RegExp(searchword, 'ig');                 /* setup input for search */
        if (searchword !== '') {
          looker = document.querySelector('#' + tbl);
          tellMe = looker.querySelectorAll('td');     /*look at all table cells of specific table*/
          for(i = 0; i < tellMe.length; i++) {
            tm = tellMe[i].innerText.toString();    
            if(tm.includes(searchword)){
              console.log(tellMe[i]);
              tellMe[i].classList.add('htSearchResult');    /*highlight partial match table cells*/
            }
          }
        }
      }"))),
  navbarPage(
    "example",
    tabPanel(
      'First Tab',
      rtable_UI('table1')
    ),
    tabPanel(
      'Second Tab',
      rtable_UI('table2')
    )
  ))

服务器的调用未更改。

server <- function(input, output, session){
  rtableServer('table1', iris)
  rtableServer('table2', mtcars)
}

shinyApp(ui, server)

enter image description here

enter image description here

更新了滚动

好的 - 此更新是关于滚动并确保文本突出显示。

我知道由于多种原因,您现在正在处理的内容并不反射(reflect)此答案中的内容。首先,我将用文字概述我所做的事情,然后编写代码(基于我原来的答案)。

我基本上拆分了函数replacer。对于滚动,您不想删除突出显示。因此,有一个包含所有代码的函数(称为 addMore),它包含除 replacer 第一行之外的所有内容。 replacer 函数只有原始的第一行,然后调用函数 addMore

当您添加函数来创建搜索栏时,表格不可用。因此,没有任何东西可以附加 onscroll 事件。因此,我创建了一个 keydown 事件,以便第一次搜索表格时,它会从 input 框中删除 keydown 事件并添加一个scroll 事件到右侧元素(小部件的第一个子元素的第一个子元素)。您可能想要使用与 keydown 不同的事件,因为您首先键入的内容都不在输入框中。 (这可能很烦人!)

首先是addMore函数

tags$script(type="text/javascript", HTML("
      function addMore(tbl) {
        var searchword = $('#' + tbl.substring(0,6) + '-searcher').val(); /* collect input */
        var custfilter = new RegExp(searchword, 'ig'); /* setup input for search */
        if (searchword !== '') {
          looker = document.querySelector('#' + tbl);
          tellMe = looker.querySelectorAll('td');   /*look at all table cells of specific table*/
          for(i = 0; i < tellMe.length; i++) {
            tm = tellMe[i].innerText.toString();    
            if(tm.includes(searchword)){
              console.log(tellMe[i]);
              tellMe[i].classList.add('htSearchResult');    /*highlight partial match table cells*/
            }
          }
        }
      }"))

新的replacer函数。

  tags$script(type="text/javascript", HTML("function replacer(tbl) {
        $('#' + tbl).first().find('.htSearchResult').removeClass('htSearchResult'); /*remove previous search highlight*/
        addMore(tbl);
      }"))

此函数用于删除 keydown 事件并添加 onscroll 事件。

tags$script(type="text/javascript", HTML("function popOnce(inid, widd){
  win = document.querySelector('input#' + inid);                                    
  /*wait for the first attempt to search to add listeners for scroll*/
  var par = win.parentElement    
  var ipar = par.children[0].outerHTML;
  var str = win.outerHTML;
  rx = /onkeydown.*\"/g;
  str = str.replace(rx, '');
  par.innerHTML = ipar + str;
  widg = document.querySelector('div#' + widd);
  where = widg.querySelector('div.ht_master > div.wtHolder');
  where.addEventListener('scroll', function(){addMore(widd)});
}")),

最后是setTimeout函数。在此函数中,我在添加 onkeyup 的同时添加了 onkeydown

tags$script(type="text/javascript", HTML("setTimeout(function(){
  var tbr = document.querySelectorAll('.tab-pane');
  tbr[1].classList.add('active'); /* so the data is avail */
  var inStr = document.querySelectorAll('input[id*=\"searcher\"]');        /*the input boxes*/
  console.log(inStr[0].getAttribute('id'));
  var widg = document.querySelectorAll('.html-widget');  /*the tables boxes*/
  for(i = 0; i < widg.length; i++) {
    var wId = widg[i].getAttribute('id');                /* collect table IDs */
    var insid = inStr[i].getAttribute('id');
    inStr[i].className = '';             /* disassociate rhandstable searcher */
    inStr[i].style.cssText = 'cursor: pointer; font-size: 1em; line-height: 1.5em; padding: 6px 12px; display: block; width: 70%;';
      /* collect label and input box; add event without rhandsontable interference */
    var par = inStr[i].parentElement    
    var ipar = par.children[0].outerHTML;
    var str = inStr[i].outerHTML;
    var html = '\"replacer(\\'' + wId + '\\')\"';
    var html2 = '\"popOnce(\\'' + insid + '\\',\\'' + wId + '\\')\"';
    /*str = str.replace('>', ' onkeyup=' + html + '>'); original before highlight scrolling */
    str = str.replace('>', ' onkeyup=' + html + ' onkeydown=' + html2 + '>');
    par.innerHTML = ipar + str;
  }}, 400)")) 

这与上面的代码相同,但它们都作为一个代码块组合在一起。

rtable_UI <- function(id) {
  ns <- NS(id)
  tagList(
    textInput(ns("searcher"), "Search"),
    rhandsontable::rHandsontableOutput(ns('table_output'))
  )
}

rtableServer <- function(id, df) {
  moduleServer(id, function(input, output, session) {
    output$table_output <- rhandsontable::renderRHandsontable({
      rhandsontable::rhandsontable(df, search = T, height = 700)
    })
  })
}

# Shiny App
ui <- tagList(
  tags$head(
    tags$style(HTML("
      .htSearchResult {
        background-color: rgb(252, 237, 217);
      }
      .rhandsontable.html-widget {
        overflow: hidden;
      }")),
    # next function was replacer—without the removing the highlighting
    tags$script(type="text/javascript", HTML("
      function addMore(tbl) {     /* collect input */
        var searchword = $('#' + tbl.substring(0,6) + '-searcher').val();
        var custfilter = new RegExp(searchword, 'ig'); /* setup input for search */
        if (searchword !== '') {
          looker = document.querySelector('#' + tbl);
          tellMe = looker.querySelectorAll('td');     /*look at all table cells of specific table*/
          for(i = 0; i < tellMe.length; i++) {
            tm = tellMe[i].innerText.toString();    
            if(tm.includes(searchword)){
              console.log(tellMe[i]);
              tellMe[i].classList.add('htSearchResult');  /*highlight partial match table cells*/
            }
          }
        }
      }")),
    tags$script(type="text/javascript", HTML("function popOnce(inid, widd){
      win = document.querySelector('input#' + inid);                                    
      /*wait for the first attempt to search to add listeners for scroll*/
      var par = win.parentElement    
      var ipar = par.children[0].outerHTML;
      var str = win.outerHTML;
      rx = /onkeydown.*\"/g;
      str = str.replace(rx, '');
      par.innerHTML = ipar + str;
      widg = document.querySelector('div#' + widd);
      where = widg.querySelector('div.ht_master > div.wtHolder');
      where.addEventListener('scroll', function(){addMore(widd)});
    }")),
    tags$script(type="text/javascript", HTML("setTimeout(function(){
      var tbr = document.querySelectorAll('.tab-pane');
      tbr[1].classList.add('active'); /* so the data is avail */
      var inStr = document.querySelectorAll('input[id*=\"searcher\"]');   /*the input boxes*/
      console.log(inStr[0].getAttribute('id'));
      var widg = document.querySelectorAll('.html-widget');  /*the tables boxes*/
      for(i = 0; i < widg.length; i++) {
        var wId = widg[i].getAttribute('id');                /* collect table IDs */
        var insid = inStr[i].getAttribute('id');
        inStr[i].className = '';             /* disassociate rhandstable searcher */
        inStr[i].style.cssText = 'cursor: pointer; font-size: 1em; line-height: 1.5em; padding: 6px 12px; display: block; width: 70%;';
          /* collect label and input box; add event without rhandsontable interference */
        var par = inStr[i].parentElement    
        var ipar = par.children[0].outerHTML;
        var str = inStr[i].outerHTML;
        var html = '\"replacer(\\'' + wId + '\\')\"';
        var html2 = '\"popOnce(\\'' + insid + '\\',\\'' + wId + '\\')\"';
        /* highlight scrolling modification */
        str = str.replace('>', ' onkeyup=' + html + ' onkeydown=' + html2 + '>');
        par.innerHTML = ipar + str;
      }}, 400)")),
  tags$script(type="text/javascript", HTML("function replacer(tbl) {
        $('#' + tbl).first().find('.htSearchResult').removeClass('htSearchResult'); /*remove previous search highlight*/
        addMore(tbl);
      }"))),
  navbarPage(
    "example",
    tabPanel(
      'First Tab',
      rtable_UI('table1')
    ),
    tabPanel(
      'Second Tab',
      rtable_UI('table2')
    )
  ))

server <- function(input, output, session){
  rtableServer('table1', iris)
  rtableServer('table2', mtcars)
}

shinyApp(ui, server)

enter image description here

关于r - 在 Shiny 的应用程序中搜索多个 rhandsontable 的功能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69969852/

相关文章:

r - 如何在 R 中以概率方式合并两个向量

r - 错误: processing vignette . ..: 'what'必须是字符串或函数

r - 如何在 mutate_at 中取消引用拼接?

r - Shiny 的 splitLayout 和 selectInput 问题

r - 在 Shiny 应用程序中自定义 rhandsontable 颜色的正确方法

r - ggplot2:向网格添加对角线

python - R Shiny 并行处理,调用Python脚本

R Shiny 数据不可用 "Error: object not found"

r - 在 Shiny 的应用程序中存储 RHandsontable 的编辑