preSubmit firing twice on form submit cancel - (DT 1.10.16, Editor 1.6.5)

preSubmit firing twice on form submit cancel - (DT 1.10.16, Editor 1.6.5)

maliwikmaliwik Posts: 55Questions: 5Answers: 1

Versions:
jQuery 3.2.1
DataTables 1.10.16
Editor 1.6.5

I'm trying to create a prompt that asks people if they intended to delete a record if Deleted is selected from a status dropdown box (delete is actually updating a status column in the database, not an actual SQL delete). I've been trying to get it to work consistently, but I keep running into preSubmit firing twice.

I have the following code as a test:

editor.on( 'preSubmit', function ( e, data, action ) {
  alert('test');
  return false;
});

Whenever I submit the form, it alerts me with "test" twice, no matter what. If I add conditional code inside to check for if it's Deleted as in the following code:

// make sure user intended to delete record
editor.on( 'preSubmit', function ( e, data, action ) {
  if(editor.field( '__my status field__' ).input().val() == '5'){
    if(!confirm('Are you sure you want to delete this record?')){
      return false;
    }else{
      return true;
    };
  }
});

It'll pop up asking me the question. If I hit "cancel", it'll ask me again. If I hit cancel again, it'll submit the form the next time I try to submit.

I'm stumped, and I'm hoping it's not a typo like last time you helped me (I seriously checked for that, I swear hah).

This question has accepted answers - jump to:

Answers

  • Rob BrunRob Brun Posts: 56Questions: 2Answers: 14

    Hi maliwik maybe check if the action is an update like

    editor.on( 'preSubmit', function ( e, data, action ) {
    
    if(action === 'edit'){
      alert('test');
      return false;
    }
    
    });
    
    

    I know that I have experienced weird behavior in the preSubmit event when I neglect to specify the action.

    Shay

  • allanallan Posts: 63,552Questions: 1Answers: 10,476 Site admin

    One other thing - are you pressing return or clicking on the button in the confirm dialogue? Try clicking on it rather than pressing return. I'm wondering if the return might be propagated through to the document.

    Beyond that, can you link to a test case?

    Allan

  • maliwikmaliwik Posts: 55Questions: 5Answers: 1

    Rob, I added that for consistency-sake. Thanks!

    Allan,

    That is indeed what it is doing. Here is my test code:

        editor.on( 'preSubmit', function ( e, data, action ) {
            if(action === 'edit' && this.field( '__my status field__' ).input().val() == '5'){
                confirm('Are you sure you want to delete this record?')
            }
            return false;
        });
    

    When clicking the button everything behaves normally, but when hitting Return instead it asks twice.

    Do you know if there might be a fix for this?

  • allanallan Posts: 63,552Questions: 1Answers: 10,476 Site admin

    What browser are you using? With Chrome 60 if I run the following on this page

    editor.on( 'preSubmit', function ( e, data, action ) {
        console.log( 'preSubmit event' );
        if(action === 'edit'){
            confirm('Are you sure you want to delete this record?')
        }
        return false;
    });
    

    It seems to work as expected. It will never submit an edit since it is always returning false, but that's useful in this case to test.

    Allan

  • maliwikmaliwik Posts: 55Questions: 5Answers: 1

    I'm currently using Opera and the script checks to see if someone has selected the Deleted status from the Status dropdown box. I don't actually delete the data from the database, it just sets it so it's invisible from the end-user. I've been trying to figure out how to do this via the Editor remove button with PHP in the backend, but I've been unable to do so.

  • allanallan Posts: 63,552Questions: 1Answers: 10,476 Site admin
    Answer ✓

    Use an edit action - not a delete. You can label it as "Delete" in the UI if you want, but really you are just editing the data so it doesn't match a condition.

    To make it simple what I would normally recommend for this is to use a second Editor instance which just has the condition field. When the button is pressed (custom button documentation) use edit() to start editing, message() to ask the user if they want to do the delete, buttons() to define the buttons at the bottom of the form and val() to set the field's value. When submitted, if it no longer matches the where condition, it will be removed from the table.

    Allan

  • maliwikmaliwik Posts: 55Questions: 5Answers: 1
    edited September 2017

    Thanks Allan!

    I used the following code to add to the DataTables buttons declaration:

    "buttons": [
        {
            "name": 'commands',
            "extend": "create",
            "editor": editor,
            "text": '<i class="fa fa-plus-circle"></i>&nbsp;&nbsp;Create New User (Shortcut: C)',
            "key": 'C'
        },
        {
            "name": 'commands',
            "extend": "edit",
            "editor": editor,
            "text": '<i class="fa fa-pencil"></i>&nbsp;&nbsp;Edit Selected User (Shortcut: E)',
            "key": 'e'
        },
        {
            "name": 'commands',
            "extend": "edit",
            "editor": editor,
            "text": '<i class="fa fa-times-circle"></i>&nbsp;&nbsp;Delete Selected User (Shortcut: D)',
            "key": 'd',
            "action": function ( e, dt, node, config ) {
                deleteRow = confirm('Are you sure you want to delete this user? Please double-check and make sure this is your intention.');
                if(deleteRow){
                    editor
                        .edit(table.rows( { selected: true } ).indexes(), false )
                        .set( 'tbl_Users.Status_ID', '5' )
                        .submit(
                            function (data) {
                                if(data['cancelled']){
                                    alert( 'This user cannot be deleted.' );
                                }else{
                                    alert( 'User successfully deleted.' );
                                } // endelse (data['cancelled'])
                            } // endfunction (data)
                        ); // end submit()
                } // endif(deleteRow)
            } // end "action"
        }, // end button
    
        'pageLength'
    ]
    

    I then did some simple logic in the PHP backend like such:

    // Edit record routine
    ->on( 'preEdit', function ( $editor, $id, $values ) {
    
        // check if we're deleting the user
        if($values['tbl_Users']['Status_ID'] == '5'){
    
            // make sure the user's status is able to be modified
            if($values['tbl_Users']['User_StatusIsModifiable']){
                $editor
                    ->field( 'tbl_Users.Status_ID' )
                    ->setValue( '5' )
                    ->set( true );
            }else{
                return false;
            } // endif($values['tbl_Users']['User_StatusIsModifiable'])
    
        } // endif($values['tbl_Users']['Status_ID'] == '5')
    
    }) // end preEdit (Edit record routine)
    

    Now it'll also make sure that the user can be modified (we don't want our client accidentally deleting themselves hehe :smile:) and if they can't, it'll let them know that the selected user cannot be deleted.

    In Firefox Developers edition and Opera at least, the key button presses still fire twice. It'll ask me if I want to delete twice if I hit the D shortcut key but I'm not sure how to fix that part.

  • allanallan Posts: 63,552Questions: 1Answers: 10,476 Site admin

    Try adding e.stopPropagation() and e.preventDefault() at the top of your action function.

    Allan

  • maliwikmaliwik Posts: 55Questions: 5Answers: 1

    That didn't seem to fix it unfortunately.

  • allanallan Posts: 63,552Questions: 1Answers: 10,476 Site admin

    I'm not sure what would cause that and haven't been able to reproduce it here. Are you able to link to a test case please?

    Thanks,
    Allan

  • maliwikmaliwik Posts: 55Questions: 5Answers: 1

    It's in an administration section. Can I e-mail you some test login credentials?

  • allanallan Posts: 63,552Questions: 1Answers: 10,476 Site admin

    You can drop me a private message by clicking my name above and then the "Send message" button.

    Thanks,
    Allan

  • maliwikmaliwik Posts: 55Questions: 5Answers: 1
    Answer ✓

    With some detective work by Allan, I figured out that if you use the B dom element more than once within the dom option when declaring the DataTables object such as "dom": '<"#tableButtonsTop.tableButtons"Bf>iprtip<"#tableButtonsBottom.tableButtons"B>',, it will fire the key defined by the key option however many times your button groups exists when declaring the buttons. This happens in at least Opera and Firefox Developer Edition.

    I'm uncertain how to get around this behavior.

  • allanallan Posts: 63,552Questions: 1Answers: 10,476 Site admin

    Ah - I missed an element of that before and it explains why I was a bit confused before. Don't use B twice in the dom string. That would use the buttons configuration object twice, which is what is causing this issue.

    You'd need to add your second set of Buttons using the method shown in this example.

    Allan

  • maliwikmaliwik Posts: 55Questions: 5Answers: 1

    Is there a way to clone the button group instead of creating everything twice? Because I just want to have the same exact button group both above and below the table.

  • maliwikmaliwik Posts: 55Questions: 5Answers: 1
    edited October 2017

    Basically what I did is created a function that just makes the buttons according to a few options and appends them to a container. It's a bit messy, but it works. I think perhaps allowing for the insertion of as many button groups initially defined within the main DataTables configuration object through the B element in the dom would be pretty handy if it's possible.

    Here are the functions for inserting the group and deletion of record via an edit instead of delete action if anybody wants to use it or modify it to their needs:

    // create commands datatables button group
    function insertCommandButtonGroup(table, editor, groupName, container, addKeys){
    
        // create button object
        new $.fn.dataTable.Buttons( table, {
            "name": groupName,
            "buttons": [
                {
                    "extend": "create",
                    "editor": editor,
                    "text": '<i class="fa fa-plus-circle"></i>&nbsp;&nbsp;Create New Category (Shortcut: C)',
                    "key": (addKeys ? 'c' : false)
                }, // end create button
                {
                    "extend": "editSingle",
                    "editor": editor,
                    "text": '<i class="fa fa-pencil"></i>&nbsp;&nbsp;Edit Selected Category (Shortcut: E)',
                    "key": (addKeys ? 'e' : false)
                }, // end edit button
                {
                    "extend": "editSingle",
                    "editor": editor,
                    "text": '<i class="fa fa-times-circle"></i>&nbsp;&nbsp;Delete Selected Category (Shortcut: D)',
                    "key": (addKeys ? 'd' : false),
                    "action": function(e, mode, action){
                        deleteSelectedCategory(table, editor, e, mode, action);
                    }// end action
    
                }  // end delete button
            ]
        });
    
        // append table buttons
        table.buttons(groupName, null).containers()
                 .appendTo( container );
    
    } // endfunction insertCommandButtonGroup
    
    // delete selected category by setting its status to '5' (hidden from users) in the database
    function deleteSelectedCategory(table, editor, e, mode, action){
        e.stopPropagation();
        e.preventDefault();
    
        selected = editor.edit(table.rows( { selected: true } ).indexes(), false );
        categoryIsProtected = selected.get('tbl_User_Report_Categories.User_Rep_Cat_IsProtected');
        categoryTitle = 'Category title:   ' + selected.get('tbl_User_Report_Categories.User_Rep_Cat_Title');
    
        if(categoryIsProtected == '1'){
    
            alert( 'The following report category is protected and cannot be deleted:\n\n' +
                categoryTitle );
    
        }else{
    
            deleteRow =
                confirm( 'Are you sure you want to delete the following report category?\n\n' +
                  categoryTitle + '\n\n' +
                  'Please double-check and make sure this is your intention.\n' +
                  'Deleting a report category will not delete the reports within.');
    
            if(deleteRow){
    
                selected.set('tbl_User_Report_Categories.Status_ID', '5')
                    .submit(
                        function (data) {
                            alert( 'The following report category has been successfully deleted:\n\n' +
                    categoryTitle );
                            table.ajax.reload();
                        }, // endfunction (data)
                        null,
                        function (data) {
                            data.Status_ID = '5';
                        }
                    ); // end submit()
    
            } // endif(deleteRow)
    
        } // endelse(categoryIsProtected == '1')
    
    } // endfunction deleteSelectedCategory
    

    The parameters are self explanatory, although addKeys should be false for all but the first call of the function to avoid triggering the button-action event any subsequent times.

    To do it this way, dom must not have any B elements in it, and buttons should be set to false in the original DataTables declaration.

    The following is a short snippet of what I used to insert the buttons into the dom:

    insertCommandButtonGroup(tableReportCategories, editorReportCategories, 'commandsTop', '#tableButtonsTop', true);
    insertCommandButtonGroup(tableReportCategories, editorReportCategories, 'commandsBottom', '#tableButtonsBottom', false);
    

    tableReportCategories is my DataTables object, editorReportCategories is my Editor object, commandsTop and commandsBottom are the two names of the groups, #tableButtonsTop and #tableButtonsBottom are the names of the containers I want the button groups to be inserted to, and true and false are the addKeys options - the keypress triggers will be added to the first one only (true).

    I hope this can help anybody else out who's having issues with this.

    Thanks for your help Allan and Rob!

    -- Mike

  • allanallan Posts: 63,552Questions: 1Answers: 10,476 Site admin

    Yup - that looks just about the perfect way to work around this. Thanks for positing your solution.

    I'll look at putting something into Buttons to handle this case as I think it should just work as expected out of the box.

    Allan

  • allanallan Posts: 63,552Questions: 1Answers: 10,476 Site admin

    Just in case anyone else stumbles across this, Buttons 1.5.0 will address this issue directly. If buttons over multiple instances for the same table share a key handler, only the first will now trigger.

    Allan

This discussion has been closed.