0

I am trying to add a function call "onblur" where i can type a new value in the TD cell. What I need is the new value to be passes by the function to a other Jquery script. My problem is that the datatable wont see the This value as it seems the code is not written correctly. What am I doing wrong? I cant find anything that helped me so far..

This is the php version that works, this is what I am trying to implent in the Datatable table.

<td
    contenteditable="true"
    data-old_value="name"
    onBlur="saveInlineEdit(this,'name','23')"
    onClick="highlightEdit(this);"
>
    name
</td>

Concrete. How do i use the new typed value as "this" in the folowing line, or how do i implement the code that works in the HTML table in the jQuery DataTable?

var options = {
    data: 'my_data',
    render: function ( data, type, row, meta ) {
        return '<div onBlur="saveInlineEdit('this.innerHTML,'name', + row.data_id + ') " onClick="highlightEdit(this);"><font color='+row.cat_color+'>'+data+'</font></div>';
    }
}

The part in the DataTables script to add the attributes:

createdRow: function (row, data, dataIndex) {
    $('td:eq(4)',row).attr('contenteditable',true);
    $('td:eq(4)',row).attr('data-old_value', data.bullets);
}

I want to use the following script to post the value of the saveInlineEdit function

function highlightEdit(editableObj) {
    $(editableObj).css("background","");
} 

function saveInlineEdit(editableObj,column,id) {
    // no change change made then return false
    if($(editableObj).attr('data-old_value') === editableObj.innerHTML) {
        return false;
    }
    // send ajax to update value
    $(editableObj).css("background","#FFF url(loader.gif) no-repeat right");
    $.ajax({
        url: "update_inlinedata.php",
        cache: false,
        data:'column='+column+'&value='+editableObj.innerHTML+'&id='+id,
        success: function(response)  {
            console.log(response);
            // set updated value as old value
            $(editableObj).attr('data-old_value',editableObj.innerHTML);
            $(editableObj).css("background","");            
        }
    });
}
miken32
  • 42,008
  • 16
  • 111
  • 154
Martin15789
  • 83
  • 1
  • 9
  • After an edit you need to update the row data stored in plugin api and call another draw – charlietfl Jun 05 '21 at 22:24
  • Thanks for your reaction. My problem is not that. my problem is that the line seems to error or something. I need to know how to define the THIS part in this line onBlur="saveInlineEdit('this.innerHTML,'name', + row.data_id + ') " onClick="highlightEdit(this);" So I need help how to put the right dots and commas or even how to acces the by user typed new value to send to the update script – Martin15789 Jun 05 '21 at 22:42
  • When asking questions about client-side behaviour, DO NOT include PHP code. Use the rendered HTML that the PHP outputs. – miken32 Jun 05 '21 at 23:23
  • You should not be using 1990's style scripting attributes like `onblur`. Use jQuery event code to set this up. This can also help ensure that event handlers are properly bound to newly created elements as well. See the answers to [this question](https://stackoverflow.com/questions/203198/event-binding-on-dynamically-created-elements) for details. – miken32 Jun 05 '21 at 23:25
  • Thank you for your reaction. I am just learning this, can someone help me on my way to make that post posible with code that is not 1990 than ? The example you give is not helping me achieve what i now have.. – Martin15789 Jun 05 '21 at 23:35
  • I may be misunderstanding the question, but... The `this.innerHTML` you refer to is the data contained in the render function's `data` parameter (you would have to remove any HTML tags embedded in the `` cell yourself, though). I assume `row.data_id` gives you the row's unique identifier - and you already have that in your code. And you can use `meta.col` to get the cell's column index (zero-based). Does that give you all the info you need, to know which specific field in which specific record was updated for your POST update? – andrewJames Jun 06 '21 at 01:20
  • Thanks but no. The update did not happen yet! I trying to make the update happen with inline editing. The html row makes the cell editable. The user can begin typing. What the user types is what needs to be activated with the function higlightedit and saveinlineedit. That (this) part is me trying to get that new input the user typed to update the cell. That php code is what i need transformed to datatable code – Martin15789 Jun 06 '21 at 07:16
  • The row.data id. Is the id from the row sql table. This is there so the script can post the id so I can update it with sql. WHERE id is.. – Martin15789 Jun 06 '21 at 07:18

1 Answers1

1

There are a couple of different pieces to your question - the following covers the capturing of changed cell data, and making sure the DataTable reflects the edits made in the DOM by the user.

(I did not tackle the highlighting, but I think you can extend the below approach to cover that as well, since it's handling the same data.)

I think using a createdCell option in a columnDef may be a bit easier than using a createdRow, because you will get direct access to the column's value:

columnDefs: [ {
targets: 4,
createdCell: function (td, cellData, rowData, rowIdx, colIdx) {
  // 'td' is the DOM node, not the DataTable cell
  td.setAttribute('contenteditable', true);
  td.setAttribute('spellcheck', false);
  td.setAttribute('data-old_value', cellData);
  td.addEventListener("focus", function(e) {
    original = e.target.textContent;
  })
  td.addEventListener("blur", function(e) {
    if (original !== e.target.textContent) {
      console.log( 'row ID: ', rowData.id );
      console.log( 'new DOM value: ', td.innerHTML);
      // 'cell' is the DataTable cell, not the DOM node:
      let cell = $('#example').DataTable().cell(rowIdx, colIdx);
      console.log( 'before cell update: ', cell.data() );
      cell.data(td.innerHTML);
      console.log( 'after cell update: ', cell.data() );
    }
  })
}
} ]

Acknowledgement: The above approach is modified from the one shown in this answer.

Here is a self-contained demo:

var my_data = [
    {
      "id": "123",
      "name": "Tiger Nixon",
      "position": "System Architect",
      "salary": "$320,800",
      "bullets": "lorem ipsum",
      "office": "Edinburgh",
      "extn": "5421"
    },
    {
      "id": "456",
      "name": "Donna Snider",
      "position": "Customer Support",
      "salary": "$112,000",
      "bullets": "dolor sit amet",
      "office": "New York",
      "extn": "4226"
    }
  ];

$(document).ready(function() {

var table = $('#example').DataTable( {
  data: my_data,
  columns: [
    { title: "ID", data: "id" },
    { title: "Name", data: "name" },
    { title: "Office", data: "office" },
    { title: "Position", data: "position" },
    { title: "Bullets", data: "bullets" },
    { title: "Extn.", data: "extn" },
    { title: "Salary", data: "salary" }
  ],
columnDefs: [ {
targets: 4,
createdCell: function (td, cellData, rowData, rowIdx, colIdx) {
  // 'td' is the DOM node, not the DataTable cell
  td.setAttribute('contenteditable', true);
  td.setAttribute('spellcheck', false);
  td.setAttribute('data-old_value', cellData);
  td.addEventListener("focus", function(e) {
    original = e.target.textContent;
  })
  td.addEventListener("blur", function(e) {
    if (original !== e.target.textContent) {
      console.log( 'row ID: ', rowData.id );
      console.log( 'new DOM value: ', td.innerHTML);
      // 'cell' is the DataTable cell, not the DOM node:
      let cell = $('#example').DataTable().cell(rowIdx, colIdx);
      console.log( 'before cell update: ', cell.data() );
      cell.data(td.innerHTML);
      console.log( 'after cell update: ', cell.data() );
    }
  })
}
} ]


} ); 

} );
<head>
  <meta charset="UTF-8">
  <title>Demo</title>
  <script src="https://code.jquery.com/jquery-3.5.1.js"></script>
  <script src="https://cdn.datatables.net/1.10.22/js/jquery.dataTables.js"></script>
  <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.22/css/jquery.dataTables.css">
  <link rel="stylesheet" type="text/css" href="https://datatables.net/media/css/site-examples.css">

</head>

<body>

<div style="margin: 20px;">

    <table id="example" class="display dataTable cell-border" style="width:100%">
    </table>

</div>

</body>

Update

I don't have a server which can handle your ajax call, so I cannot test a "success" response. Having said that, here are my notes:

For the saveInlineEdit function, you will no longer need this:

if($(editableObj).attr('data-old_value') === editableObj.innerHTML) {
  return false;
}

This is because you have already performed that check in the event listener:

if (original !== e.target.textContent) { ... }

Also, you have already determined what the new value of the cell is - so you might as well just pass that directly to the function:

saveInlineEdit(td, 'bullets', rowData.id, cell.data());

The above line needs to be placed in the event listener shown above:

td.addEventListener("blur", function(e) {
  if (original !== e.target.textContent) {
    console.log( 'row ', rowIdx, ' col ', colIdx );
    console.log( 'row ID: ', rowData.id );
    console.log( 'new DOM value: ', td.innerHTML);
    // 'cell' is the DataTable cell, not the DOM node:
    let cell = $('#example').DataTable().cell(rowIdx, colIdx);
    console.log( 'before cell update: ', cell.data() );
    cell.data(td.innerHTML);
    console.log( 'after cell update: ', cell.data() );
    let columnName = $('#example').DataTable().settings();
    console.log( 'column name: ', columnName );

    saveInlineEdit(td, 'bullets', rowData.id, cell.data()); // NEW LINE HERE
  }
})

Your saveInlineEdit function therefore changes, to reflect the above points:

I removed the unnecessary if condition.

I added an extra parameter newValue - since we don;t need to keep retrieving it from the cell (we've already done that).

function saveInlineEdit(editableObj, column, id, newValue) {
  console.log( 'in ajax call' );
  console.log(editableObj);
  console.log(column);
  console.log(id);
  console.log(newValue);
  // send ajax to update value
  $(editableObj).css("background","#FFF url(loader.gif) no-repeat right");
  $.ajax({
    url: "update_inlinedata.php",
    cache: false,
    data:'column=' + column + '&value=' + newValue + '&id=' + id,
    success: function(response)  {
      console.log(response);
      // set updated value as old value
      $(editableObj).attr('data-old_value', newValue);
      $(editableObj).css("background","");            
    }
  });
}

I put logging statements into the function, so you can see what the parameters are.

So, for example, the query parameter data submitted by the ajax call will be:

column=bullet&value=lorem%20ipsum%20editedbyme&id=123

And just to say again, I cannot test this ajax call - so bear that in mind, i case I made a stupid mistake there, somewhere.


That leaves 2 additional point which are outside the scope of the question, but which need to be considered:

  1. The question assumes only column index 4 is editable. If you want every cell in a row to be editable you need to enhance this to use the relevant column names. One good way to do this is to use the DataTables name option:

    { title: "Bullets", data: "bullets", name: "bullets" },

This value can be retrieved and used by the blur event handler, before you call your saveInlineEdit function:

let columnName = $('#example').DataTable().settings()[0].aoColumns[colIdx].sName;

Then your call becomes:

saveInlineEdit(td, columnName, rowData.id, cell.data());
  1. The current code updates the data in the DataTable here:

    cell.data(td.innerHTML);

This happens before the return from the ajax call. If that call fails then you have updated data in your data table, but not in the back end database. So you may want to move that logic around to ensure the DataTable data is updated only in the event of a successful ajax call.

andrewJames
  • 19,570
  • 8
  • 19
  • 51
  • Thank you. But I needed those atriubutes to perform ajax update. At what point can I use these to post the values row.data.id(id of the sql date recource so not the one generated by datatables) name( stands for the colomn name in the sql database), new value to the update php? – Martin15789 Jun 06 '21 at 16:50
  • I assumed you would want to post the changes as part of the `onblur` event. Is that not correct? – andrewJames Jun 06 '21 at 16:58
  • Yes I do. When onblur post new value, colomn name(assigned as name in my try out), row.id . If colomnname or id is not part of the post the sql statement wouldnt know what colomn or id to update. Maybe i dont know enough to see but that seems to be not in the code? If so that my mistake – Martin15789 Jun 06 '21 at 17:31
  • It's not in the code because I did not think that was part of the question: "_My problem is that the datatable wont see the `this` value as it seems the code is not written correctly._". Your question already includes the jQuery `ajax` call. Can you not use that, but with the new edited data value you now have access to? – andrewJames Jun 06 '21 at 17:36
  • I dont think so because within the function with the this those three atributes are there to post. They now are not so they wont be posted. They where supposed the be posted in my line of code when calling the function. You did not made the this value in that line work. You made a whole new option for me, there for a lot of thanks. But when those two are not there it wont work I am afraid :(. That is why I asked the question, in my opinion when the function as inline part worked with those values it was done. – Martin15789 Jun 06 '21 at 17:47
  • I am sorry - I did not understand that comment. If you find a solution, I'd be happy to vote for it. – andrewJames Jun 06 '21 at 18:13
  • Thanks. I wont find a solution as i dont have enough knowledge to do so. That is why i am asking for help. What about the comment did i not make clear? For the update to work this function should be working. onBlur="saveInlineEdit('this.innerHTML,'name', + row.data_id + ') – Martin15789 Jun 06 '21 at 18:17
  • I suppose I should have said _"if someone finds a solution for you..."_. I hope that happens. As one last attempt by me, I have provided some additional notes in my answer. They are my attempt to clarify my (not clear enough!) first answer, for you.... – andrewJames Jun 06 '21 at 20:14
  • ...My answer is that you can use your existing `saveInlineEdit()` function because we have already handled that troublesome `this` value that you are asking about - we have replaced it with the actual cell, and value that are being edited. So, we have all the information we need. At least, that is my approach. Just to add: there is no meaningful way to use `this` in a column renderer - which is what your question shows. Again, apologies for not being able to help you more easily. And I do hope you find (or get) a way forward. – andrewJames Jun 06 '21 at 20:15
  • Thank you verry much !, I tried your update live and it works ! I now have but one question left and that is part of the things you said to consider. Can I make the colomnname dynamic? let columnName = $('#example').DataTable().settings(); console.log( 'column name: ', columnName ); saveInlineEdit(td, 'bullets', rowData.id, cell.data()); // Hardcoded column name saveInlineEdit(td, columnName , rowData.id, cell.data()); // Tried like this did not work – Martin15789 Jun 06 '21 at 21:00
  • Thanks again, you made my day ! – Martin15789 Jun 06 '21 at 21:48
  • Sorry for bothering you again. I have been trying to work on point 2. Nothing is working, either I wont get the value or its updating the table before as it was. The current code updates the data in the DataTable here: cell.data(td.innerHTML); This happens before the return from the ajax call. If that call fails then you have updated data in your data table, but not in the back end database. So you may want to move that logic around to ensure the DataTable data is updated only in the event of a successful ajax call. – Martin15789 Jun 07 '21 at 09:23
  • Take a look at this Fiddle: https://jsfiddle.net/yqvprkuo/ It contains 2 URLs, just for testing the ajax. Comment out one of them and test with the other. One is for "success" the other is for "failure". It should give you some guidance on handling the cell updates, I hope. – andrewJames Jun 07 '21 at 13:54