Format a vector of rows in italic and red font in R DT (datatable)

后端 未结 3 855
执念已碎
执念已碎 2020-12-11 13:07

A bit similar to this question: How to give color to a given interval of rows of a DT table?

but in my case I would like to let the user select rows in the table, th

相关标签:
3条回答
  • 2020-12-11 13:45

    Here is an attempt.

    library(shiny)
    library(DT)
    
    rowCallback <- function(rows){
      c(
        "function(row, data, num, index){",
        sprintf("  var rows = [%s];", paste0(rows-1, collapse = ",")),
        "  if(rows.indexOf(index) > -1){",
        "    for(var i=1; i<data.length; i++){",
        "      $('td:eq('+i+')', row)",
        "        .css({'background-color': 'rgb(211,211,211)', 'font-style': 'italic'});",
        "    }",
        "  }",
        "}"  
      )
    }
    
    ui <- fluidPage(
      actionButton('SubmitRemoval', 'Exclude selected rows'),
      actionButton('UndoRemoval', 'Include full data'),
      br(),
      DTOutput('mytable')
    
    )
    
    server <- function(input, output,session) {
    
      output[["mytable"]] <- renderDT({
        input[["SubmitRemoval"]]
        input[["UndoRemoval"]]
        rows <- isolate(input[["mytable_rows_selected"]])
        datatable(mtcars, 
                  options = list(
                    rowCallback = JS(rowCallback(rows))
                  )
        )
      })
    
      proxy <- dataTableProxy("mytable")
    
      observeEvent(input[["UndoRemoval"]], { 
        proxy %>% selectRows(NULL)
      })
    
    }
    
    shinyApp(ui, server)
    

    0 讨论(0)
  • 2020-12-11 13:55

    Here is a better solution (it took me several hours). This one does not redraw the table when one clicks the button, and it doesn't go wrong when one sorts the table by a column.

    library(shiny)
    library(DT)
    
    initComplete <- c(
      "function(settings) {",
      "  var table=settings.oInstance.api();", 
      "  $('#SubmitRemoval').on('click', function(){",
      "    table.$('tr.selected').addClass('x');",
      "  });",
      "  $('#UndoRemoval').on('click', function(){",
      "    table.$('tr').removeClass('x');",
      "  });",
      "}"
    )
    
    callback <- "
    var xrows = [];
    table.on('preDraw', function(e, settings) {
      var tbl = settings.oInstance.api();
      var nrows = tbl.rows().count();
      var rows = tbl.$('tr');
      var some = false; var r = 0;
      while(!some && r<nrows){
        if($(rows[r]).hasClass('x')){
          some = true
        }
        r++;
      }
      if(some){
        xrows = [];
        for(var i = 0; i < nrows; i++){
          if($(rows[i]).hasClass('x')){
            xrows.push(rows[i].getAttribute('id'));
          }
        }
      }
    }).on('draw.dt', function(){
      for(var i=0; i<xrows.length; i++){
        var row = $('#' + xrows[i]);
        row.addClass('x');
      }
      xrows = [];
    });
    "
    
    ui <- fluidPage(
      tags$head(
        tags$style(HTML(
          ".x { background-color: rgb(211,211,211) !important; font-style: italic}
           table.dataTable tr.selected.x td { background-color: rgb(211,211,211) !important;}"
        ))
      ),
      actionButton('SubmitRemoval', 'Exclude selected rows'),
      actionButton('UndoRemoval', 'Include full data'),
      br(),
      DTOutput('mytable')
    
    )
    
    server <- function(input, output,session) {
    
      dat <- cbind(mtcars[1:6,], id=1:6)
    
      output[["mytable"]] <- renderDT({
        datatable(dat, 
                  callback = JS(callback),
                  options = list(
                    initComplete = JS(initComplete),
                    rowId = JS(sprintf("function(a){return a[%d];}", ncol(dat))), 
                    columnDefs = list(list(visible=FALSE, targets=ncol(dat)))
                  )
        )
      })
    
      proxy <- dataTableProxy("mytable")
    
      observeEvent(input[["UndoRemoval"]], { 
        proxy %>% selectRows(NULL)
      })
    
    }
    
    shinyApp(ui, server)
    

    Update

    Here is the version including icons:

    library(shiny)
    library(DT)
    
    initComplete <- c(
      "function(settings) {",
      "  var table = settings.oInstance.api();", 
      "  var cross = '<span style=\"color:red; font-size:18px\"><i class=\"glyphicon glyphicon-remove\"></i></span>'",
      "  var checkmark = '<span style=\"color:red; font-size:18px\"><i class=\"glyphicon glyphicon-ok\"></i></span>'",
      "  $('#SubmitRemoval').on('click', function(){",
      "    table.$('tr.selected').addClass('x');",
      "    table.$('tr.selected')",
      "      .each(function(){$(this).find('td').eq(1).html(cross);});",
      "  });",
      "  $('#UndoRemoval').on('click', function(){",
      "    table.$('tr').removeClass('x');",
      "    table.$('tr')",
      "      .each(function(i){$(this).find('td').eq(1).html(checkmark);});",
      "  });",
      "}"
    )
    
    callback <- "
    var cross = '<span style=\"color:red; font-size:18px\"><i class=\"glyphicon glyphicon-remove\"></i></span>'
    var xrows = [];
    table.on('preDraw', function(e, settings) {
      var tbl = settings.oInstance.api();
      var nrows = tbl.rows().count();
      var rows = tbl.$('tr');
      var some = false; var r = 0;
      while(!some && r<nrows){
        if($(rows[r]).hasClass('x')){
          some = true
        }
        r++;
      }
      if(some){
        xrows = [];
        for(var i = 0; i < nrows; i++){
          if($(rows[i]).hasClass('x')){
            xrows.push(rows[i].getAttribute('id'));
          }
        }
      }
    }).on('draw.dt', function(){
      for(var i=0; i<xrows.length; i++){
        var row = $('#' + xrows[i]);
        row.addClass('x').find('td').eq(1).html(cross);
      }
      xrows = [];
    });
    "
    
    ui <- fluidPage(
      tags$head(
        tags$style(HTML(
          ".x { background-color: rgb(211,211,211) !important; font-style: italic}
           table.dataTable tr.selected.x td { background-color: rgb(211,211,211) !important;}"
        ))
      ),
      actionButton('SubmitRemoval', 'Exclude selected rows'),
      actionButton('UndoRemoval', 'Include full data'),
      br(),
      DTOutput('mytable')
    
    )
    
    server <- function(input, output,session) {
    
      dat <- cbind(Selected = '<span style="color:red; font-size:18px"><i class="glyphicon glyphicon-ok"></i></span>', 
                   mtcars[1:6,], id = 1:6)
    
      output[["mytable"]] <- renderDT({
        datatable(dat, 
                  escape = -2, 
                  callback = JS(callback),
                  options = list(
                    initComplete = JS(initComplete),
                    rowId = JS(sprintf("function(data){return data[%d];}", ncol(dat))), 
                    columnDefs = list(
                      list(visible = FALSE, targets = ncol(dat)),
                      list(className = "dt-center", targets = "_all")
                    )
                  )
        )
      })
    
      proxy <- dataTableProxy("mytable")
    
      observeEvent(input[["UndoRemoval"]], { 
        proxy %>% selectRows(NULL)
      })
    
    }
    
    shinyApp(ui, server)
    

    Update

    To get the indices of the excluded rows in input$excludedRows:

    initComplete <- c(
      "function(settings) {",
      "  var table = settings.oInstance.api();", 
      "  var cross = '<span style=\"color:red; font-size:18px\"><i class=\"glyphicon glyphicon-remove\"></i></span>'",
      "  var checkmark = '<span style=\"color:red; font-size:18px\"><i class=\"glyphicon glyphicon-ok\"></i></span>'",
      "  $('#SubmitRemoval').on('click', function(){",
      "    table.$('tr.selected').addClass('x');",
      "    table.$('tr.selected')",
      "      .each(function(){$(this).find('td').eq(1).html(cross);});",
      "    var excludedRows = [];",
      "    table.$('tr').each(function(i, row){",
      "      if($(this).hasClass('x')){excludedRows.push(parseInt($(row).attr('id')));}",
      "    });",
      "    Shiny.setInputValue('excludedRows', excludedRows);",
      "  });",
      "  $('#UndoRemoval').on('click', function(){",
      "    table.$('tr').removeClass('x');",
      "    table.$('tr')",
      "      .each(function(i){$(this).find('td').eq(1).html(checkmark);});",
      "    Shiny.setInputValue('excludedRows', null);",
      "  });",
      "}"
    )
    

    Update

    This is easier with the option server = FALSE of renderDT:

    library(shiny)
    library(DT)
    
    initComplete <- c(
      "function(settings) {",
      "  var table = settings.oInstance.api();", 
      "  $('#SubmitRemoval').on('click', function(){",
      "    table.$('tr.selected').addClass('x').each(function(){",
      "      var td = $(this).find('td').eq(1)[0];", 
      "      var cell = table.cell(td);", 
      "      cell.data('remove');",
      "    });",
      "    table.draw(false);",
      "    table.rows().deselect();",
      "    var excludedRows = [];",
      "    table.$('tr').each(function(i, row){",
      "      if($(this).hasClass('x')){excludedRows.push(parseInt($(row).attr('id')));}",
      "    });",
      "    Shiny.setInputValue('excludedRows', excludedRows);",
      "  });",
      "  $('#UndoRemoval').on('click', function(){",
      "    table.$('tr').removeClass('x').each(function(){",
      "      var td = $(this).find('td').eq(1)[0];", 
      "      var cell = table.cell(td);", 
      "      cell.data('ok');",
      "    });",
      "    Shiny.setInputValue('excludedRows', null);",
      "  });",
      "}"
    )
    
    render <- c(
      'function(data, type, row, meta){',
      '  if(type === "display"){',
      '    return "<span style=\\\"color:red; font-size:18px\\\"><i class=\\\"glyphicon glyphicon-" + data + "\\\"></i></span>";',
      '  } else {',
      '    return data;',
      '  }',
      '}'
    )
    
    ui <- fluidPage(
      tags$head(
        tags$style(HTML(
          ".x { color: rgb(211,211,211); font-style: italic; }"
        ))
      ),
      verbatimTextOutput("excludedRows"),
      actionButton('SubmitRemoval', 'Exclude selected rows'),
      actionButton('UndoRemoval', 'Include full data'),
      br(),
      DTOutput('mytable')
    )
    
    server <- function(input, output,session) {
    
      dat <- cbind(Selected = "ok", mtcars[1:6,], id = 1:6)
    
      output[["mytable"]] <- renderDT({
        datatable(dat, 
                  extensions = "Select",
                  options = list(
                    initComplete = JS(initComplete),
                    rowId = JS(sprintf("function(data){return data[%d];}", ncol(dat))), 
                    columnDefs = list(
                      list(visible = FALSE, targets = ncol(dat)),
                      list(className = "dt-center", targets = "_all"),
                      list(
                        targets = 1,
                        render = JS(render)
                      ) 
                    )
                  )
        )
      }, server = FALSE)
    
      proxy <- dataTableProxy("mytable")
    
      observeEvent(input[["UndoRemoval"]], { 
        proxy %>% selectRows(NULL)
      })
    
      output$excludedRows <- renderPrint({
        input[["excludedRows"]]
      })
    
    }
    
    shinyApp(ui, server)
    
    0 讨论(0)
  • 2020-12-11 14:06

    Here is a variant. Instead of using a button to mark the selected rows as removed, the user clicks on the icons.

    library(shiny)
    library(DT)
    
    callback <- c(
      "table.on('click', 'td:nth-child(2)', function(){",
      "  var td = this;",
      "  var cell = table.cell(td);",
      "  if(cell.data() === 'ok'){",
      "    cell.data('remove');",
      "  } else {",
      "    cell.data('ok');",
      "  }",
      "  var $row = $(td).closest('tr');",
      "  $row.toggleClass('excluded');",
      "  var excludedRows = [];",
      "  table.$('tr').each(function(i, row){",
      "    if($(this).hasClass('excluded')){",
      "      excludedRows.push(parseInt($(row).attr('id')));",
      "    }",
      "  });",
      "  Shiny.setInputValue('excludedRows', excludedRows);",
      "})"
    )
    
    restore <- c(
      "function(e, table, node, config) {",
      "  table.$('tr').removeClass('excluded').each(function(){",
      "    var td = $(this).find('td').eq(1)[0];", 
      "    var cell = table.cell(td);", 
      "    cell.data('ok');",
      "  });",
      "  Shiny.setInputValue('excludedRows', null);",
      "}"
    )
    
    render <- c(
      'function(data, type, row, meta){',
      '  if(type === "display"){',
      '    return "<span style=\\\"color:red; font-size:18px\\\"><i class=\\\"glyphicon glyphicon-" + data + "\\\"></i></span>";',
      '  } else {',
      '    return data;',
      '  }',
      '}'
    )
    
    ui <- fluidPage(
      tags$head(
        tags$style(HTML(
          ".excluded { color: rgb(211,211,211); font-style: italic; }"
        ))
      ),
      fluidRow(
        column(
          6, 
          tags$label("Excluded rows"),
          verbatimTextOutput("excludedRows")
        ),
        column(
          6, 
          tags$label("Included rows"),
          verbatimTextOutput("includedRows")
        )
      ),
      br(),
      DTOutput('mytable')
    )
    
    server <- function(input, output,session) {
    
      dat <- cbind(Selected = "ok", mtcars[1:6,], id = 1:6)
    
      output[["mytable"]] <- renderDT({
        datatable(dat, 
                  extensions = c("Select", "Buttons"), 
                  selection = "none", 
                  callback = JS(callback),
                  options = list(
                    rowId = JS(sprintf("function(data){return data[%d];}", ncol(dat))), 
                    columnDefs = list(
                      list(visible = FALSE, targets = ncol(dat)),
                      list(className = "dt-center", targets = "_all"),
                      list(className = "notselectable", targets = 1),
                      list(targets = 1, render = JS(render)) 
                    ),
                    dom = "Bt",
                    buttons = list("copy", "csv",
                                   list(
                                     extend = "collection",
                                     text = 'Select all rows', 
                                     action = JS(restore)
                                   )
                    ),
                    select = list(style = "single", selector = "td:not(.notselectable)")
                  )
        )
      }, server = FALSE)
    
        output$excludedRows <- renderPrint({
          input[["excludedRows"]]
        })
    
        output$includedRows <- renderPrint({
          setdiff(1:nrow(dat), input[["excludedRows"]])
        })
    
    }
    
    shinyApp(ui, server)
    

    0 讨论(0)
提交回复
热议问题