Problem with Duplicate Change Events in DataTables After Copying a Record

Problem with Duplicate Change Events in DataTables After Copying a Record

srinu1222srinu1222 Posts: 6Questions: 1Answers: 0
edited July 12 in Free community support

Problem:
We have built a feature in DataTables that allows users to copy an existing record. This feature lets users edit only the required fields (approximately 20+ fields) assuming that the majority of the data remains the same. However, this requires the user to change at least 4 to 5 dropdown fields at a minimum on the "copied" record.

The copy function works fine, but after a successful copy, the dropdown change events are being triggered twice. This means each field that we change takes twice as long to process. You can imagine if there are 5 or 6 fields that require changes, this can take more than an acceptable amount of time.

Example Change Event Code:
Change Event 1: Selection of “entity” field triggering data fetching for “function” field

javascript
Copy code

$(document).on('change', '#DTE_Field_entity', function (e) {
    var entity = $(this).val();
    if (entity) {
        clearErrorMessage('entity');
        updateFunctionDropdown(entity);
    }
});

Change Event 2: Selection of “function” field triggering other subsequent change events

javascript
Copy code

$(document).on('change', '#DTE_Field_function', function (e) {
    var department = $(this).val();
    if (department) {
        clearErrorMessage('function');
        updateFunctionalRoleDropdown(department);
        updateOfficeLocationDropdown(department);
    }
});

Copy Feature Code:
javascript
Copy code

editor.inlineCreate('start', {
    cancelTrigger: 3,
    cancelHtml: '<i class="fa fa-times"/>',
    submitTrigger: 2,
    submitHtml: '<i class="fa fa-floppy-o"/>'
});

var tr = $(this).closest("tr");
var row = table.row(`#${$(tr).attr("id")}`).data();
var fields = editor.displayed();

for (var i = 0; i < fields.length; i++) {
    editor.field(fields[i]).val(row[fields[i]]);
}

Question:
Is there a way to prevent the change events from being triggered twice? Is there an alternative to the editor.inlineCreate function that might help avoid this issue?

Any guidance on the above would be greatly appreciated.

Edited by Kevin: Syntax highlighting. Details on how to highlight code using markdown can be found in this guide

Replies

  • kthorngrenkthorngren Posts: 21,391Questions: 26Answers: 4,961
    edited July 12

    Its difficult to understand your code structure with just the code snippets. I suspect that the change events are being created when calling the copy function. Move the change events outside the function so they execute only once. Or use jQuery off() to turn off the event before recreating it.

    If this doesn't help then please post a link to a test case replicating the issue so we can help debug.
    https://datatables.net/manual/tech-notes/10#How-to-provide-a-test-case

    Kevin

  • srinu1222srinu1222 Posts: 6Questions: 1Answers: 0
    edited July 12

    Thanks for the quick response. The change events are currently outside of the Copy function. We tried using jQuery.off()to remove the event handlers, but the problem still persists.

    Just some additional information that we missed in the original post:

    We are using the editor.inline function, which is working perfectly fine and only triggers the change event once. Here is the code for the editor.inline function:

    // Activate an inline edit on click of a table cell
    $('#fte-cost').on('click', 'tbody td.row-edit.clickable', function (e) {
        editor.inline(
            table.cells(this.parentNode, '*').nodes(),
            {
                cancelHtml: '<i class="fa fa-times"></i>',
                cancelTrigger: 'span.cancel',
                submitHtml: '<i class="fa fa-floppy-o"></i>',
                submitTrigger: 'span.edit'
            }
        );
        // Typeahead refresh
        refreshSelect();
    });
    

    However, the editor.inlineCreate function is triggering the change event twice. The code for "editor.inlineCreate" function is mentioned in the original message above.

    Any guidance on how to prevent this double triggering issue would be greatly appreciated.

    Edited by Kevin: Syntax highlighting. Details on how to highlight code using markdown can be found in this guide

  • kthorngrenkthorngren Posts: 21,391Questions: 26Answers: 4,961
    edited July 12

    I built a test case for you.
    https://live.datatables.net/guwafemu/538/edit

    Now I see what you mean regarding the events executing twice. @allan can comment on this. One option might be to use jQuery one() instead of jQuery on() and move the events inside the click event. Something like this:
    https://live.datatables.net/subucaca/1/edit

    Kevin

  • allanallan Posts: 63,602Questions: 1Answers: 10,486 Site admin

    https://live.datatables.net/guwafemu/539/edit

    I've added a check to see if inlineCreate() should be called or not. Previously it was being called when clicking into the text fields to create a new row, which was making text entry impossible.

    I'm also seeing the change event being triggered twice on the input elements. I'm not immediately sure why that would be happening.

    I'll look into that when I'm back in the office next week and let you know what I find.

    Allan

  • srinu1222srinu1222 Posts: 6Questions: 1Answers: 0
    edited July 15

    Hello Allan/Kthorngren,
    Unfortunately, the inlineCreate() method did not work. Below is our code:
    The code below uses the editor.inlineCreate method to create the "new" record and subsequently copy the data within the "new" record:
    Javascript Code:

    $('#fte-cost').on('click', 'tbody span.copy.clickable', function (e) {
       editor.inlineCreate(
           'start',
           {
               cancelTrigger: 3,
               cancelHtml: '<i class="fa fa-times"/>',
               submitTrigger: 2,
               submitHtml: '<i class="fa fa-floppy-o"/>',
               onBlur: 'none'
           }
       );
       var tr = $(this).closest("tr");
       var row = table.row(`#${$(tr).attr("id")}`).data();
       var fields = editor.displayed();
       for (var i = 0; i < fields.length; i++) {
          editor.field(fields[i]).val(row[fields[i]]);
       }
     
       // typeahead
       refreshSelect();
    });
    

    There are two issues happening with the above code:
    The "copy" record is created as the first row/record instead of "inserting" it right below the "parent" record (i.e., the record that it is copying). This is cumbersome for the user. For example, if the user is copying the 75th record, they have to drag the scroll all the way to the top to make edits/changes.

    The "Input Events" and "Change Events" are triggering twice, resulting in 2x processing time.


    We tried a different approach outlined below, but it did not work as well. Below is the summary of the approach:
    Fetch the data from the clicked copy row
    Add the record in the datatable
    Trigger the editor.inline method
    Below is the code for the above approach:

    $('#<datatable id>).on('click', 'tbody span.copy.clickable', function (e) {
       // Get data from the clicked copy row
       var tr = $(this).closest('tr');
       var rowData = table.row(`#${$(tr).attr("id")}`).data();
     
       // Duplicate the selected row data
       var newRowData = $.extend(true, {}, rowData); // Deep copy of row data
     
       // Assign a new random ID to the duplicated row
       var copiedRowId = generateRandomId();
       newRowData.id = copiedRowId;
     
       // Add the duplicated row to the table
       table.row.add(newRowData);
     
       // Get the current data of the table
       var allData = table.data().toArray();
     
       // Find the index of the parent row
       var parentRowIndex = table.row(tr).index();
     
       // Move the new row data after the parent row index
       var copiedRowData = allData.pop();
       allData.splice(parentRowIndex + 1, 0, copiedRowData);
     
       // Clear the table and add the new data
       table.clear().rows.add(allData).draw();
     
       // Find the new row and trigger a click event on the 'row-edit clickable' cell
      $(`#${copiedRowId}`).find('td.row-edit.clickable').trigger('click');
    });
    

    Our Observation from the above code
    In the above approach, we expected that the "Cancel" for the "Copy" feature would work as "delete." However, it is working as "Cancel" when "editing" a record.
    It appears that the above approach can work if we have a "click event" for the "Cancel" feature. This will allow us to write logic to delete the record from the datatable.


    Below are the requirements from end-user perspective for the “Copy” feature:

    Req# 1 (Works): When the user clicks Copy, create a duplicate record.
    Req# 2 (Works): When the user clicks Copy and Saves without editing the record, a message box pops up stating “Record already exist”.

    Req# 3 (Works): When the user clicks Copy, edits the data (in dropdown or input field) and saves the record, the record should successfully save.

    Req# 4 (DOES NOT WORK): When the user clicks Copy and then "Cancel" the record with or without editing the record, the "Cancel" button for the "Copy" feature should work like the "Cancel" button of the "New" feature. However, it seems the "Cancel" click is not triggering.

    Below is the javascript code for event triggering for cancel on copy.

    // Handle the click event on the cancel icon within the copied row
    $('#<datatable id> span.cancel').click(function (e) {
       e.stopPropagation(); // Prevent event bubbling
     
       var tr = $(this).closest('tr');
       var rowId = $(tr).attr("id");
     
       if (rowId == copiedRowId) {
           // Remove the row from the DataTable
           table.row(`#${rowId}`).remove().draw();
       }
    });
    

    We tried the above two approaches. Is there something we need to change in our approach, or is there any new approach to achieve the "Copy" functionality?

    Edited by allan to add syntax highlighting. See the documentation here.

  • allanallan Posts: 63,602Questions: 1Answers: 10,486 Site admin

    The "copy" record is created as the first row/record instead of "inserting" it right below the "parent" record (i.e., the record that it is copying).

    The inline creation row is a "ghost". It doesn't (yet) exist, and therefore it cannot take part in the filtering or ordering that is applied to the DataTable. Indeed, if you consider this example, when you click on "Create" and it shows the new row, it now has 11 rows on that page, rather than 10 as the pagination for the table suggests it should.

    At the moment inlineCreate() has options only to show the new row at the start or the end of the table body. There is no option to show it next to an existing row. To be honest, this is the first time I've come across that requirement, and I think it is a good point. It does seem like a useful thing for inlineCreate() to be able to do.

    I've added that to my list of things to add and I would hope to get that into Editor for the 2.4 release. A workaround would be to shuffle the DOM elements around after inlineCreate() has been called.

    The "Input Events" and "Change Events" are triggering twice, resulting in 2x processing time.

    I've yet to look into that. I'll report back once done.

    Allan

  • srinu1222srinu1222 Posts: 6Questions: 1Answers: 0

    Thanks, Allan!

    We will try the workaround and report back with our findings. We appreciate you looking into the “triggering twice” issue.

    Could you also please let us know your thoughts on our alternative approach mentioned above, where Requirement #4 is not working? Repasting the code below:

    // Handle the click event on the cancel icon within the copied row
    $('#<datatable id> span.cancel').click(function (e) {
    e.stopPropagation(); // Prevent event bubbling
    var tr = $(this).closest('tr');
    var rowId = $(tr).attr("id");
    if (rowId == copiedRowId) {
    // Remove the row from the DataTable
    table.row(#${rowId}).remove().draw();
    }
    });

    The positive aspect of the above code is that it resolves both the row positioning and “triggering twice” issues. It also meets Requirements # 1 to 3 mentioned above. However, it does not meet Requirement #4. If we can find a solution for Requirement #4, this approach could serve as a good workaround until you operationalize release 2.4.

    has context menu

    has context menu

  • allanallan Posts: 63,602Questions: 1Answers: 10,486 Site admin

    Hi,

    Been looking into this and indeed the inlineCreate() code is calling the set action twice, which is what is causing the change event to happen twice. I've committed a fix for this which will be in the next release of Editor.

    If you'd like to hot fix the code, find:

        $.each(this.s.fields, function(name, fieldIn) {
            fieldIn.multiReset();
            fieldIn.multiSet(0, fieldIn.def());
            fieldIn.set(fieldIn.def());
        });
    

    and change to be:

        $.each(this.s.fields, function(name, fieldIn) {
            fieldIn.multiReset();
            fieldIn.multiSet(0, fieldIn.def());
        });
    

    Req# 4 (DOES NOT WORK): When the user clicks Copy and then "Cancel" the record with or without editing the record, the "Cancel" button for the "Copy" feature should work like the "Cancel" button of the "New" feature. However, it seems the "Cancel" click is not triggering.

    Apologies, I'm having a hard time picturing this. This is referring to the cancelTrigger button that is setup by inlineCreate()? When you click on the element / column specified by cancelTrigger the inline create row should just disappear. Is that not what is happening? It does appear to work in this little example: https://live.datatables.net/guwafemu/543/edit (final column does a cancel.

    Thanks,
    Allan

  • srinu1222srinu1222 Posts: 6Questions: 1Answers: 0

    when will you release the above requirement?

  • allanallan Posts: 63,602Questions: 1Answers: 10,486 Site admin

    With the next release of Editor. I'm not yet sure if that will be a patch or moving on to 2.4. I'll look at the changes and I think there is a good chance I'll do a patch release next week.

    Allan

Sign In or Register to comment.