Editor Inline Create

Editor Inline Create

william.moorewilliam.moore Posts: 1Questions: 1Answers: 0

Is there a way to extend Editor to allow for in-line create?

«1

Answers

  • kthorngrenkthorngren Posts: 21,117Questions: 26Answers: 4,916

    This is not directly supported but this thread should point you in the right direction.
    https://datatables.net/forums/discussion/32858

    Kevin

  • colincolin Posts: 15,237Questions: 1Answers: 2,598

    Hi @william.moore ,

    This thread might also help as well - the example shows how to create an empty row then inline edit immediately.

    Cheers,

    Colin

  • hnhegdehnhegde Posts: 68Questions: 17Answers: 0

    Hi All,
    I am facing this challenge as well. I invoke a REST api with certain mandatory fields. So creating (ie, POST'ing) an empty row and then immediately editing is not an option. Hence, I tried to hack around by adding an empty row just on the client side and then submitting it via Editor's edit() functionality. But the challenge is how to handle the actual row edit where I need to send a 'PATCH' http request? I explored dynamically deciding the http verb to send based on a condition, like:

    edit: {
        type: () => { if <condition> return 'POST' else return 'PATCH'},
        url: ".......",
        .................
    }
    

    That doesn't seem work. Gives out a jQuery error.

    Uncaught TypeError: s.type.toUpperCase is not a function
        at Function.ajax (jquery.js:9322)
        at Editor._ajax (dataTables.editor.js:4532)
        at Editor._submit (dataTables.editor.js:5591)
        at send (dataTables.editor.js:3710)
        at Editor.submit (dataTables.editor.js:3732)
        at HTMLDocument.<anonymous> (dataTables.editor.js:5120)
        at HTMLDocument.dispatch (jquery.js:5237)
        at HTMLDocument.elemData.handle (jquery.js:5044)
    

    Kindly advise how to set the http verb dynamically.

    Regards,
    Harsha

  • allanallan Posts: 63,075Questions: 1Answers: 10,385 Site admin
    edited October 2019

    I don't believe jQuery allows the type to be a function. If you need to be able to change the verb for edit dynamically you'd need to use ajax as a function - e.g.:

    edit: function ( method, url, data, success, error ) {
      $.ajax({
        url: '...',
        type: condition ?
          'POST' :
          'PATCH',
        url: ...,
        data: data,
        success: success
      });
    }
    

    Allan

  • hnhegdehnhegde Posts: 68Questions: 17Answers: 0

    Hi @allan ,
    Thanks for responding. I came across this thread which states we can actually change 'action' parameter. That I think would be a suitable solution for my use case. So while still using inline edit, I am trying to change 'action' to 'create' like:

    editor.on('initSubmit', function (e, action) {
                if (action === 'edit' && comp.state.inline_mode === 'create') {
                    action = 'create';
                    return action;
                } 
            });
    

    Kindly advise if this the right way. Action seems to be stuck at 'edit'.

    Regards,
    Harsha

  • colincolin Posts: 15,237Questions: 1Answers: 2,598

    Hi @hnhegde ,

    The thread you refer to was discussing preSubmit, not initSubmit. For both, the return is a boolean with false stating that the submission should be terminated.

    Cheers,

    Colin

  • hnhegdehnhegde Posts: 68Questions: 17Answers: 0

    Hi @colin
    Indeed, the thread is discussing preSubmit. I was trying to see if its possible to modify action at the stage of reading the form. Kindly advise,

    Thanks,
    Harsha

  • rf1234rf1234 Posts: 2,922Questions: 87Answers: 414
    edited October 2019

    Is there a way to extend Editor to allow for in-line create?

    I think it is more or less supported to be honest - in conjunction with the new button though.

    I have a data table with only two buttons "create" and "remove". The Editing is done inline only.

    This is the code for the two buttons. The create button creates and submits an empty row.

    var empty = false;
    
    buttons: [
        {   extend: "create", editor: ctrEventEditor,                  
                action: function ( e, dt, node, config ) {
                    empty = true;
                    var self = ctrEventEditor;
                    self.create( false )
                        .set( { 'ctr_event.ctr_id': parentId,
                                'ctr_event.event_msg': ''} )
                        .submit();
                }
        },
        {   extend: "remove", editor: ctrEventEditor },
    ],
    

    This code positions the cursor in the first field of the newly created empty row to start editing. You need to assign the respective class (here .event) to the columns in the HTML to indicate which columns to edit.

    ctrEventEditor
        .on('postCreate', function(e, json, data, id) {
            setTimeout(function () {
                if (empty) {
                    ctrEventEditor.inline(ctrEventTable.cell('#' + json.data[0].DT_RowId, '.event').node());
                    empty = false;
                }
            }, 250);
        });  
    
    <table id="tblCtrEvent" class="table table-striped table-bordered"
           cellspacing="0" width="100%">
        <thead>
            <tr>
                <th class="event"><?php echo $en?('Due Date'):('Termin Datum');?></th>
                <th class="event"><?php echo $en?('Message Text'):('Benachrichtigungstext');?></th>  
                <th class="event hideIfNotAllowed"><?php echo $en?('Private Message'):('Private Benachrichtigung');?></th> 
                <th class="event"><?php echo $en?('Email Addresses'):('E-Mail Adressen');?></th> 
            </tr>
        </thead>        
    </table>     
    

    If you don't like users leaving "garbage" in the table by creating new rows but not editing or deleting them you can do a nightly clean up batch run and delete those rows.

    Oops, I see the question had been answered already by Colin with a good example... But another example might be helpful as well ...

    Hi @william.moore ,
    This thread might also help as well - the example shows how to create an empty row then inline edit immediately.
    Cheers,
    Colin

  • hnhegdehnhegde Posts: 68Questions: 17Answers: 0

    Hi @allan ,
    Wrt your response on October 18th, using $.ajax() function almost works. But, how to specify custom ajax.dataSrc ? Kindly advise.

    Thanks,
    Harsha

  • allanallan Posts: 63,075Questions: 1Answers: 10,385 Site admin

    Hi Harsha,

    You'd use an anonymous function for the success handler:

    success: function (json) {
      success(json.dataTableData);
    }
    

    e.g. use the function to resolve (or map) the data into the format that DataTables is expecting and then pass that into the success callback.

    Allan

  • hnhegdehnhegde Posts: 68Questions: 17Answers: 0

    Noted. Thanks @allan

    Regards,
    Harsha

  • hnhegdehnhegde Posts: 68Questions: 17Answers: 0

    Hi @colin , @allan
    As per initSubmit 's doc page:

    This initSubmit event fills that gap, being triggered immediately before the data is read from the form, and giving the option of modifying the form.

    Can it be used to modify action ? If yes, can you kindly help me understand how to achieve that?

    Regards,
    Harsha

  • rf1234rf1234 Posts: 2,922Questions: 87Answers: 414
    edited October 2019

    I am very sure you can't modify the "action" parameter. @allan, @colin can you confirm pls.

    But you can return "false" from the "initSubmit" event and hence avoid anything to be sent. At the same time you should be able to submit an Editor "create" form with the values edited like this:

    yourEditor
    .on('initSubmit', function ( e, action ) {
        if ( action === 'edit' ) {
            yourCreateEditor 
                .create( false )  //no modal or bubble shown
    //fill the Editor "create" with the field values from the "edit" form.
                .set( { 'field1': yourEditor.val('field1'),
                        'field2': yourEditor.val('field2') } )
    //submit the "create" form
                .submit();
     //cancel the submission of the "edit" form!
            return false;
        }
    });
    

    Not sure whether you need to create another Editor instance "yourCreateEditor" on the client side to get this working. Probably yes. But it is not a problem to have multiple instances for the same data table.

  • allanallan Posts: 63,075Questions: 1Answers: 10,385 Site admin

    I am very sure you can't modify the "action" parameter. @allan, @colin can you confirm pls.

    You can as of the 1.9.1 release with the actionName option. On the PHP side use ->actionName('...') to tell it the new action name.

    Allan

  • rf1234rf1234 Posts: 2,922Questions: 87Answers: 414
    edited October 2019

    But that is just the name of the parameter, right. With actionName you can rename it to “widget“ for example.
    But can you change its content e.g. from “edit“ to “create“?

    But I might as well have misunderstood the entire discussion in this thread anyway. Never mind ...

  • hnhegdehnhegde Posts: 68Questions: 17Answers: 0

    Hi @rf1234 , @allan ,
    Thanks for your responses. I am actually looking to change the CRUD operation by intercepting the action in initSubmit. Say, if a certain condition is true, I want to 'create' even if the user clicks Edit. Is it possible via initSubmit?
    Regards,
    Harsha

  • rf1234rf1234 Posts: 2,922Questions: 87Answers: 414

    Well, that's what my example is about, Harsha.
    It effectively does this by replacing the “edit“ action with “create“ ...

  • hnhegdehnhegde Posts: 68Questions: 17Answers: 0

    Noted, thanks @rf1234 !

    Regards,
    Harsha

  • allanallan Posts: 63,075Questions: 1Answers: 10,385 Site admin

    But can you change its content e.g. from “edit“ to “create“?

    Ah - for that yes you could change it, but you'd need to make sure your server-side script you are using will also match whatever you set it to. Our prebuilt scripts on;y work with the constants create, edit, etc.

    Allan

  • rf1234rf1234 Posts: 2,922Questions: 87Answers: 414
    edited October 2019

    ok, but does Editor for PHP for example support this? What exactly would you need to do to make this work on the client and the server side?

    I could only think of the work around that I posted above in the "initSubmit" example which kind of plays a trick on Editor by copying the contents of the "edit" Editor instance into a new "create" Editor instance which is submitted while the "edit" submission gets canceled right after copying.

  • rf1234rf1234 Posts: 2,922Questions: 87Answers: 414
    edited October 2019

    Hi @rf1234 , @allan ,
    Thanks for your responses. I am actually looking to change the CRUD operation by intercepting the action in initSubmit. Say, if a certain condition is true, I want to 'create' even if the user clicks Edit. Is it possible via initSubmit?
    Regards,
    Harsha

    Let me give it another try! You can use "preSubmit" which is triggered right after "initSubmit". "preSubmit" allows you to manipulate the data that will be sent to the server which consist of the data read from the Editor form plus other stuff.

    It looks like this (only example I found in my code):

    You could try this. Should work, but I don't know whether the Editor server libraries will be able to process it correctly. According to @allan 's comment above it should work though.

    .on( 'preSubmit', function ( e, d, action ) {
        if ( action === 'create') {
            d.action = 'edit';
           //to be safe you can also do this, but probably not required:
            action = 'edit';
        }
    })
    

    According to the docs the 2nd parameter "d" is this:

  • rf1234rf1234 Posts: 2,922Questions: 87Answers: 414
    edited October 2019

    Just tested this with my forex data table:

    It does not work: Even though the "action" in the "data object that will be submitted to the server" is manipulated correctly Editor PHP still performs "edit" on the server side. @allan: isn't that a bug? Or are you referring to the data object within the data object, i.e. data.data or d.data in my code?

    So forget about this suggestion please. You obviously can't do this but you can still do the "work around" I described above.

  • rf1234rf1234 Posts: 2,922Questions: 87Answers: 414

    Great stuff! You could just watch me making a complete fool of myself.
    Ok, this should be the correct code:

    .on( 'preSubmit', function ( e, d, action ) {
        if ( action === 'edit' ) {
            d.action = 'create';
        }        
    })
    

    AND IT WORKS LIKE A CHARM. @allan: no bug! You can't fix the one in my mind, I guess ...

    I will post more when I develop a button that is for "edit / create" as opposed to the existing "edit" and "create" buttons.

  • rf1234rf1234 Posts: 2,922Questions: 87Answers: 414

    To whom it may concern:

    • My new "Copy & New" button
    //custom button for edit / create
        $.fn.dataTable.ext.buttons.editCreate = {
            extend: 'edit',
            text: editCreateLabel,
            className: "editCreateButton"
        };
    
    • event listener to set global variable on button click:
    var editCreate = false;
    $('.editCreateButton').click( function() {
        editCreate = true;
    } );
    
    • preSubmit Event to change edit to create if the editCreate button was hit before:
    .on( 'preSubmit', function ( e, d, action ) {
        if ( action === 'edit' && editCreate ) {
            d.action = 'create';
            editCreate = false;
        }        
    })
    
    • buttons from the data table definition:
    buttons: [
            {   extend: "create", editor: forexEditor, className: "lgfAdminOnly" },
            {   extend: "edit",   editor: forexEditor, className: "lgfAdminOnly" },
            {   extend: "editCreate", editor: forexEditor, className: "lgfAdminOnly" },
            {   extend: "remove", editor: forexEditor, className: "lgfAdminOnly" },
                        "colvis"
        ]
    
    • and this is what it looks like:

  • allanallan Posts: 63,075Questions: 1Answers: 10,385 Site admin

    Thanks for the updates. Good to hear it is working :).

    Allan

  • rf1234rf1234 Posts: 2,922Questions: 87Answers: 414

    For the sake of completeness this is the entire code - and it works now! really ...
    I had to fiddle a little with the Editor popup header content and the button label when using the new button. Since i18n for Editor is only triggered upon initialization I had to do this using jQuery. But no problem. On this occasion I discovered the "opened" event; couldn't get this working just with "on open".

    //custom button for edit / create
    $.fn.dataTable.ext.buttons.editCreate = {
        extend: 'edit',
        text: editCreateLabel,
        className: "editCreateButton"
    };
    
    var editCreate = false;
    
    var forexEditor = new $.fn.dataTable.Editor( {
        ajax: {
            url: 'actions.php?action=tblForex'
        },
        table: "#tblForex",
        fields: [ {
                label: lang === 'de' ? 'Währung:' : 'Currency:',
                name:  "forex.currency",
                type:  "select",
                options: forexCurrencyOptions
            }, {
                label: lang === 'de' ? 'Devisenkurs Datum:' : 'Exchange Rate Date:',
                name:  "forex.date",
                attr: {
                    class: dateMask
                },
                type:  "datetime",
                def:   function () { return today},
                format: 'L',
                opts:  {
                    showWeekNumber: true,
                    yearRange: 40,
                    momentLocale: momentLocale
                }
            }, {
                label: lang === 'de' ? 'Devisenkurs Währung/EUR:' : 'Exchange Rate Currency/EUR:',
                name:  "forex.rate"
            }
        ]        
    } );
    
    forexEditor
        .on('opened', function(e, type, action) {              
            if ( action === 'edit' && editCreate ) {
                $('.DTE_Header_Content').html($.fn.dataTable.Editor.defaults.i18n.create.title);
                $('.DTE_Form_Buttons button').html($.fn.dataTable.Editor.defaults.i18n.create.submit);
            } else if ( action === 'edit' ) {
                $('.DTE_Header_Content').html($.fn.dataTable.Editor.defaults.i18n.edit.title);
                $('.DTE_Form_Buttons button').html($.fn.dataTable.Editor.defaults.i18n.edit.submit);
            }
        })  
        .on('open', function(e, mode, action) {              
            maskDateTime();
            tooltipsPopups(this); 
        })  
        .on( 'preSubmit', function ( e, d, action ) {
            if ( action === 'edit' && editCreate ) {
                d.action = 'create';
                editCreate = false;
            }        
        })
        .on( 'postSubmit', function ( e, json, data, action ) {
            if (json.error) {
                if ( json.error.indexOf('1062 Duplicate entry') >= 0 ) {
                    json.error = lang === 'de' ? 
                                "Tut uns Leid, dieser Kurs existiert schon!" : 
                                "Sorry, this rate already exists!"; 
                }
            }        
        });
    
    var forexTable = $('#tblForex').DataTable( {
        dom: "Bfrltip",
        processing: true,
        serverSide: true,    //server side only works well with type "POST" !!!
        ajax: {
            url: 'actions.php?action=tblForex',
            type: 'POST'
        },
        columns: [
            {   data: "forex.currency" },
            {   data: "forex.date" },
            {   data: "forex.rate" },
            {   data: "forex.update_time" }
        ],
        columnDefs: [
            // targets may be classes
            {targets: [0, 1, 2, 3], searchable: false}
        ],
        order: [[ 1, 'desc' ]],
        select: {
            style: 'single'
        },            
        buttons: [
            {   extend: "create", editor: forexEditor, className: "lgfAdminOnly notEditCreate" },
            {   extend: "edit",   editor: forexEditor, className: "lgfAdminOnly notEditCreate" },
            {   extend: "editCreate", editor: forexEditor, className: "lgfAdminOnly" },
            {   extend: "remove", editor: forexEditor, className: "lgfAdminOnly" },
                        "colvis"
        ]
    } );
    
    forexTable
        .on ('init', function () {
            if ( ! lgfAdmin ) {
                forexTable.buttons('.lgfAdminOnly').remove();
            }
            $('*[type="search"][class="form-control input-sm"]')
                    .addClass('input-lg')
                    .css({ 'width': '400px', 'display': 'inline-block' });
            $('div.dataTables_filter').css({ 'margin-top': '1em' });
        });
        
    $('.editCreateButton').click( function() {
        editCreate = true;
    } );
    $('.notEditCreate').click( function() {
        editCreate = false;
    } );
    
  • hnhegdehnhegde Posts: 68Questions: 17Answers: 0

    Wow! Thanks so much @rf1234 ! Appreciate it!
    Regards,
    Harsha

  • rf1234rf1234 Posts: 2,922Questions: 87Answers: 414
    edited October 2019

    You are welcome! And thank you as well. You had the right idea with changing the content of the “action“ parameter.
    Already went live with something much bigger using the new button. Only took 15 minutes to make the modifications required once I had the forex table running.
    @allan, such a “copy & new“ button wouldn't it make sense to have this available as a standard Editor button?

  • allanallan Posts: 63,075Questions: 1Answers: 10,385 Site admin

    This example shows what I've called a "duplicate" button, which is basically the same thing.

    Allan

  • leprimoleprimo Posts: 25Questions: 4Answers: 0

    This example shows what I've called a "duplicate" button, which is basically the same thing.

    Is there an easy way to remove the ID in the example above so that the duplicate can be saved in the DB?

    Dirk

This discussion has been closed.