0

I have a jquery function that renders an html table from a csv file. I would like to add a column to this table that contains a button for each row. The button will need to copy certain cells in the row to the clipboard.

The csv is generated through an ajax call and therefore the rows are not static. The rows of buttons should equal the rows in the csv. How do I add this column?

I am using the clipboard.js library but I have no idea where to begin. I understand that the text that needs to be copied should be id'ed so the library knows what to copy but how do I add the id to the row in the cell? Any suggestions would be greatly appreciated.

jquery function logs.js:

function clearString(str){
   var str = str.replace(/\"/g, "");
   return str;
  }
  function csvToTable(myReturn){
    var allRows = myReturn.split(/\r?\n|\r/);
    var table = '<table class="tablesorter">';
    for (var singleRow = 0; singleRow < allRows.length; singleRow++) {
      if (singleRow === 0) {
        table += '<thead>';
        table += '<tr>';
      } else {
        table += '<tr>';
      }
      var rowCells = allRows[singleRow].split(',');
      for (var rowCell = 0; rowCell < rowCells.length; rowCell++) {
        if (singleRow === 0) {
          table += '<th style="padding: 15px;">';
          table += clearString(rowCells[rowCell]);
          table += '</th>';
        } else {
          table += '<td>';
          table += clearString(rowCells[rowCell]);
          table += '</td>';
        }
      }
      if (singleRow === 0) {
        table += '</tr>';
        table += '</thead>';
        table += '<tbody>';
      } else {
        table += '</tr>';
      }
    }
    table += '</tbody>';
    table += '</table>';
    return table;
  }
  function showLogData(name, domElement, csv){
    csv = csv || false;

    var selStatus = jQuery('#txtselstatus').val();
    jQuery('#returndatalog').css('display', 'none');
    if (selStatus == "")
    {
      alert("Please enter status")
      jQuery('#txtselstatus').focus();
    }else
    {
      jQuery(domElement).html("<img src='/static/img/loader.gif'/>");
      jQuery.ajax({
        type: "POST",
        url: '/ajax/generatelogdata',
        dataType: 'json',
        data: {
            "chkstatus":"",
            'place_id': place_id,
            'queryname': name,
            'startDate': $("#reportrange").data('daterangepicker').startDate.format("YYYY-MM-DD"),
            'endDate': $("#reportrange").data('daterangepicker').endDate.format("YYYY-MM-DD")
        },
        success: function(response){
          ///console.log(response)
           jQuery('#returndatalog').css('display', 'block');

          if(typeof response =='object')
          {
            var myReturn = response.response;
            if (csv) {
                jQuery(domElement).html(csvToTable(myReturn));
                jQuery(domElement + " table").tablesorter();
            }
            else {
                jQuery(domElement).html(response.response.replace(/\"/g,""));
            }
          }
          if(response.error){

          }
          else{

          }
        },
        error:function(response){

        }
      });
    }
  }

html:

{% block css_includes %}
<link rel="stylesheet" href="/static/css/jquery-ui.css" type="text/css" />
<link rel="stylesheet" href="/static/css/tablesorter/style.css?rand=1" type="text/css" />
{% endblock %}
{% block js_includes %}
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript" src="/static/js/admin_stats.js?v={{version}}"></script>
<script src="/static/js/jquery-ui.js" type="text/javascript"></script>
<script  type="text/javascript"  src="/static/js/admin.js?v={{version}}"></script>
<script src="/static/js/handlebars.min-v1.1.2.js" type="text/javascript"></script>
<script  type="text/javascript"  src="/static/js/clipboard.min.js"></script>
<!-- datepicker add -->
<script type="text/javascript" src="//cdn.jsdelivr.net/jquery/1/jquery.min.js"></script>
<script type="text/javascript" src="//cdn.jsdelivr.net/momentjs/latest/moment.min.js"></script>
<script  type="text/javascript"  src="/static/js/jq.tablesorter.js"></script>
<script  type="text/javascript"  src="/static/js/logs.js?rand=9"></script>
<link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/bootstrap/3/css/bootstrap.css" />
<script type="text/javascript" src="//cdn.jsdelivr.net/bootstrap.daterangepicker/2/daterangepicker.js"></script>
<link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/bootstrap.daterangepicker/2/daterangepicker.css" />
{% endblock %}
{% block body %}
{% include "csr_header.html" %}
<!--Styles-->


    <!--Scripts-->
<script>
var place_id = ''; 
var startDate = '{{request.GET.start_date}}';
var endDate = '{{request.GET.end_date}}';

var clipboard = new Clipboard('.btn');

clipboard.on('success', function(e) {
    console.info('Action:', e.action);
    console.info('Text:', e.text);
    console.info('Trigger:', e.trigger);

    e.clearSelection();
});

clipboard.on('error', function(e) {
    console.error('Action:', e.action);
    console.error('Trigger:', e.trigger);
});

function init() {
    showLogData('globallogs_places', '#returngloballogsplaces', true);
}
</script>
<br>
<h3>Select a Date</h3>
<br>
<div id="reportrange" class="pull-left" style="background: #fff; cursor: pointer; padding: 5px 10px; border: 1px solid #ccc; width: 23%">
    <i class="glyphicon glyphicon-calendar fa fa-calendar"></i>&nbsp;
    <span></span> <b class="caret"></b>
</div>
<br>
<h3>Errors: Places</h3>
<div class="pull-right">
<a href="https://console.cloud.google.com/logs/viewer?" style="color:#60B7BA;
    font-size: 20px;
    margin-top: 0;
    padding-top: 11px;">View Logs</a>
</div>
<br>
<br>
<div id="returngloballogsplaces" align="left"></div>
<br>
<br>
<br>
{% endblock %}
Prof. Falken
  • 499
  • 6
  • 21

2 Answers2

3

The clipboard.js plugin cares about binding the "what to copy" with the buttons using the data-clipboard-target attribute. So an id has to be defined inline.

You can do it using a unique id generated in the table creation loop, using the singleRow index.

Now, what's to be copied has to be an input... Not a td, because clipboard gets the text to copy using the value... Not the inner HTML.

You can style your input so it doesn't look like an input! Use the beginning of the id to style it. It is disabled in the generated markup I suggest.

input[id^='toCopy']{
  border:0;
  outline:0;
}

Then, you just have to instantiate the plugin on the button class. Here I strongly suggest you use a class that has no chance to be already used somewhere else. In their documentation, they suggest .btn... But that is way too common. I suggest .cb_btn (cb as in clipboard).

Below is your csvToTable function modified.
Notice the cellToCopyIndex index that you will have to check. It has to be a zero-based integer corresponding to the column you wish to copy.

function csvToTable(myReturn){

  var cellToCopyIndex = 2;  // The index of the cell to be copied, zero based

  var allRows = myReturn.split(/\r?\n|\r/);
  var table = '<table class="tablesorter">';
  for (var singleRow = 0; singleRow < allRows.length; singleRow++) {
    if (singleRow === 0) {
      table += '<thead>';
      table += '<tr>';
    } else {
      table += '<tr>';
    }
    var rowCells = allRows[singleRow].split(',');
    for (var rowCell = 0; rowCell < rowCells.length; rowCell++) {
      if (singleRow === 0) {
        table += '<th style="padding: 15px;">';
        table += clearString(rowCells[rowCell]);
        table += '</th>';
      } else {
        table += '<td>';
        if(rowCell==cellToCopyIndex){
          table += '<input type="text" id="toCopy_'+singleRow+'" value="'+clearString(rowCells[rowCell])+'" readonly>';
        }else{
          table += clearString(rowCells[rowCell]);
        }
        table += '</td>';
      }
    }
    if (singleRow === 0) {
      table += '</tr>';
      table += '</thead>';
      table += '<tbody>';
    } else {
      table += '</td><td><button class="cb_btn" data-clipboard-target="#toCopy_'+singleRow+'"><img src="assets/clippy.svg" alt="Copy to clipboard"></button></td></tr>';
    }
  }
  table += '</tbody>';
  table += '</table>';
  return table;
}

To instantiate the plugin, do it when the table is created... That is in your Ajax success callback:

success: function(response){
  ///console.log(response)
   jQuery('#returndatalog').css('display', 'block');

  if(typeof response =='object')
  {
    var myReturn = response.response;
    if (csv) {
        jQuery(domElement).html(csvToTable(myReturn));
        jQuery(domElement + " table").tablesorter();
        new Clipboard('.cb_btn');                       // Instantiate Clipboard here.
    }
    else {
        jQuery(domElement).html(response.response.replace(/\"/g,""));
    }
  }
  if(response.error){

  }
  else{

  }
},

Disclaimer: Fully untested!! ;)

Louys Patrice Bessette
  • 33,375
  • 6
  • 36
  • 64
  • I just edited about a quote mistake... And I suddenly notice the `return table;` at the end... If the function is to be used to create multiple tables, you'll need to pass a table number to use along the `singleRow`... To make sure the generated `id` are unique! **Have fun!** – Louys Patrice Bessette Jan 06 '18 at 21:15
  • Just a stupid taught: Have you considered the use of [DataTable](https://datatables.net/) where adding columns is way easier? ;) – Louys Patrice Bessette Jan 06 '18 at 21:18
  • Edited again... About where to instantiate clipboard.js. That is in you Ajax `success` callback. – Louys Patrice Bessette Jan 06 '18 at 22:52
  • I am getting this error when I click the button and it appears to be comming from the clipboard.js. I am still new to jquery so I am not sure what I should be looking for. The column was created and generates the correct rows, the copy to clipboard is just now working. `Uncaught DOMException: Failed to execute 'querySelector' on 'Document': '#toCopy_'+singleRow+'' is not a valid selector. at e.t [as target] ` – Prof. Falken Jan 07 '18 at 14:38
  • Thanks for letting me know about DataTable, I will look at replacing my current design with this library in the near future. I like the layout and themes as well the simpler customization. – Prof. Falken Jan 07 '18 at 14:42
  • That is a quote issue. The rendered id should be `toCopy_12`. (12 is just an example) It looks like `singleRow` was outputted as a string instead of outputing its value. Look closely in my suggested code, there is some single quotes and double quotes. – Louys Patrice Bessette Jan 07 '18 at 14:42
  • Found it, fixed it, thanks! Now I am getting this error. `Uncaught Error: Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute` I tried to look it up online but nothing specific generated. Thoughts? – Prof. Falken Jan 07 '18 at 14:53
  • Ha!. Change `disabled` for `readonly` in the table creation loop. If you don't find it, look in the edit history ( the `edited x second ago` link below my answer). It is highlighted in green (scroll the code to the right). – Louys Patrice Bessette Jan 07 '18 at 14:56
  • @louys-patruce-bessette That did it! Final question. You said that I could hide the styling of the id in the input but it appears not to be working. I need to place the style of the input on the page so it does not interfere with the rest of the application. Where should I insert the styling? – Prof. Falken Jan 07 '18 at 15:08
  • The style I suggest may not be enought... Like if you have a background color behind the input... Then you shoud apply the same color to the input's background... It should be placed in between `` – Louys Patrice Bessette Jan 07 '18 at 15:20
  • Perfect. Thanks! – Prof. Falken Jan 07 '18 at 16:06
1

I have added an image that points out the places in your code where:

  • the rows start | This is a good spot for an id if you really need it...
  • the rows end | This is where you could add a cell with a button in it.

I think I wouldn't add ids... I'd detect the proper parent element of the button click and go from there.

A pic of your code with some arrows added...

JasonB
  • 6,243
  • 2
  • 17
  • 27
  • So my code for the button row should be the line after the else {? Something like this: `} else { table +=' – Prof. Falken Jan 06 '18 at 20:41
  • Upvoted your answer since you were on the right track ;) – Louys Patrice Bessette Jan 06 '18 at 21:27