Change to inline edit on next line with press of a key [local & serverSide processing]

Change to inline edit on next line with press of a key [local & serverSide processing]

cudzich09cudzich09 Posts: 12Questions: 2Answers: 1
edited July 2018 in Free community support

Hello,

I would like to post a test case but I have no idea how to link server-side scripts to the live site.

In a nutshell, while in inline editing mode, pressing enter should trigger a function that will send edited rows to the server for saving and jump to next line in table, focus and activate inline editing of the first cell.

Setup:
Datatable instance ( w/ keyTables)
localEditor instance
ajaxEditor instance

Datatables paramters

[
'scrollY' => 500,
'paging' => false,
'processing' =>  false,
'responsive'  => true,
'keys' => [
   'columns' => [1,2,3,4,5,6,7],
   'editor' => 'localEditor',
   'editOnFocus' => true,
   'className' => 'highlight',
]


    // My view 
    $(function() {
        // Datatables and Editor global variables
        var table;
        var localEditor;
        var ajaxEditor;


        // Editor options
        var editorOpts = {
          table: "#items",
          fields: [
              {label: "Item Number", name: "item_number"},
              {label: "Unit Id", name: "unit_id"},
              {label: "Quantity", name: "quantity"},
              {label: "Dating", name: "dating"},
              {label: "Location", name: "warehouse_location_codes"},
              {label: "Prv. Loc.", name: "prv_warehouse_location_codes"},
              {label: "Note", name: "note"},
          ],
        };


        // Array to store local edits for queue
        var changedRows = [];
        var duplicatesRemovedChangedRows;


        // Editor instance to carry out updates on changed rows.
        ajaxEditor = new $.fn.dataTable.Editor( $.extend( true, {
                ajax: "/items_editor/2",
            },
            editorOpts ) );


        // Editor instance to store local changes
        localEditor = new $.fn.dataTable.Editor( $.extend( true, {
          formOptions: {
            inline: {
              drawType: 'none'
              }
            }
          },
          editorOpts ) );


        // After submitting edit to dt table, store row number in array for later submission.
        localEditor.on('postEdit', function(e, json, data) {
          changedRows.push( '#'+data.DT_RowId );
        } );


        // Activate inline editor when clicking on datatable cells.
        $('#items').on('click', 'tbody td:not(:first-child)', function (e) {
            localEditor.inline( this );
        });


        // Datatable initialization
        {{$dataTable->generateScripts()}}


        // Obtain datatables api instance.
        table = $('#items').DataTable();


    //On enter, submit changed rows to server.
    table.on( 'key', function ( e, datatable, key, k, cell, originalEvent ) {
      if( key === 13 && changedRows.length !== 0 ) { // return
        duplicatesRemovedChangedRows = Array.from(new Set(changedRows));

        // Submit changes to server through ajaxEditor instance
        ajaxEditor
          .edit(duplicatesRemovedChangedRows, false)
          .submit();

        // Clear out changedRows array
        changedRows.length = 0;
      }
    });


    });

I had inline editing activated with serverside processing but the trip to the server and back ( 2 ajax requests ) lags when tabbing between columns. Therefore I opted out to try localEditor, store edited rows in an array and trigger ajaxEditor when I press an Enter key. All of that is working with the above code.

Now, the majority of the time users will only enter data into the first 4 columns, I would like to enable the enter key to send data (if present in array ) to the server and jump to next line.

If I change the contents of the cell I have to press Enter key twice in order to trigger the ajaxEditor.
Is there a way to just trip the ajaxEditor on first enter press?

This produces stopPropogation error but moves cell to next line.

                    // localEditor.on( 'open', function ( e, mode, action ) {
                    //     if ( mode === 'inline' ) {
                    //         localEditor.on( 'postSubmit.editorInline', function () {
                    //             var focused = table.cell( { focused: true } );
                    //
                    //             if ( focused.any() ) {
                    //                 var next = $(focused.node()).next();
                    //
                    //                 if ( next.length ) {
                    //                     localEditor.one( 'submitComplete', function () {
                    //                         table.cell( next ).focus();
                    //                     } );
                    //                 }
                    //             }
                    //         } );
                    //     }
                    // } );
                    //
                    // localEditor.on( 'close', function (event) {
                    //     localEditor.off( 'postSubmit.editorInline' );
                    // } );

I'm looking at the following examples to solve this issue, but are having a hard time moving forward due to "Type Error: Cannot read property 'stopPropagation' of null.

https://www.datatables.net/forums/discussion/comment/105467/
https://datatables.net/blog/2017-10-24

When pressing enter key twice, focus moves to the next cell but inline is not activated and the error of 'stopPropogation' is popping out in the console.

ToDo:
1. Trigger ajaxEditor to send changed rows data to the server on first 'Enter' keydown when in inline mode.
2. Move inline focus to next line, first cell and activate it.

Any help is appreciated, again I wish I could provide a test case.
Michael

Answers

  • cudzich09cudzich09 Posts: 12Questions: 2Answers: 1

    Here is the inline editor in action.

    As you can see the localEditor is successfully saving edited rows in an array, ajaxEditor then uses the array to access datatables instances retrieve rows and send to server for processing.

    The Enter key moves the focus to next cell but without activating inline editing + the error :(

  • allanallan Posts: 63,455Questions: 1Answers: 10,465 Site admin

    Hi Michael,

    1. Trigger ajaxEditor to send changed rows data to the server on first 'Enter' keydown when in inline mode.

    That's odd. You should only need to press the return key once as shown in this example. Does that example work for you?

    1. Move inline focus to next line, first cell and activate it.

    Listen for the submitComplete and in there get the current row index for the item that was focused and then listen for draw (since you are using server-side processing you need to wait for the Ajax draw to complete) - something like:

    editor.on( 'submitComplete', function () {
      var rowsCurrentOrder = table.rows( { order:'display' } ).ids();
      var editRowIdx = rowsCurrentOrder.indexOf( editor.ids()[0] );
      var nextRowId = rowsCurrentOrder[ editRowIdx + 1 ];
    
      table.on( 'draw', function () {
        editor.inline( nextRowId );
      } );
    } );
    

    Its a few hoops to jump through because of the async nature of server-side processing and also the fact that row nodes are destroyed and created as needed.

    I've not actually tested the above yet, so its possible I've done something daft, but that's the general idea!

    Allan

  • cudzich09cudzich09 Posts: 12Questions: 2Answers: 1
    edited July 2018

    @allan Thanks for the sample code.

    Implementing editor.ids() returns editor.ids is not a function

    I moved on to tackle this from a different angle.

    Step 1: In 'postEdit' of the localEditor I successfully obtain next row id.
    Step 2: In 'submitComplete' of the ajaxEditor and table on draw I attempt to set the inline editor to next row id but an exception is thrown. Specifically:
    "Uncaught Cannot edit more than one row inline at a time"

    I attempted to blur both the local and ajax editor along with disabling keys before calling localEditor.inline (nextRowId, 'columnName') without success.

    Here is the code, I feel like I'm close to achieving what I need.
    I should mention, data is being successfully submitted to the server, its the inline focus that needs to be set afterwords that is now working.

    What say you, Mr. Allan? Thanks

            //On enter, submit changed rows to server.
            table.on( 'key', function ( e, datatable, key, k, cell, originalEvent ) {
              if( key === 13 && changedRows.length !== 0 ) { // return
                duplicatesRemovedChangedRows = Array.from(new Set(changedRows));
    
                // Submit changes to server through ajaxEditor instance
                ajaxEditor
                  .edit(duplicatesRemovedChangedRows, false)
                  .submit();
    
                // Clear out changedRows array
                changedRows.length = 0;
              }
            });
    
            ajaxEditor.on( 'submitComplete', function() {
              table.on( 'draw', function() {
                ajaxEditor.blur();
                localEditor.blur();
                table.keys.disable();
                localEditor.inline( nextRowId, 'item_number' );
                table.keys.enable();
              } );
            } );
    
  • cudzich09cudzich09 Posts: 12Questions: 2Answers: 1
    edited July 2018

    I have partially resolved the "uncaught cannot edit more than one row inline at a time" exception. It was caused by re-declaring nextRowId in helper function instead of assigning the value to a global. Hence an undefined and 'item_number' were being passed into editor.inline().

    Moving on...

                    ajaxEditor.on('submitComplete', function() {
                    table.on( "draw", function( settings ) {
                            localEditor.inline(nextRowId, 'item_number' );
            } );
    


    = Uncaught TypeError: Cannot read property 'contents' of undefined

    I'm passing in the id of the row to be activated for inline editing without success.

  • allanallan Posts: 63,455Questions: 1Answers: 10,465 Site admin

    Okay - so what is happening now is that you must have server-side processing enabled? This example shows how you can get inline editing to work with server-side processing - you need to pass the row or cell index into the inline() method - e.g.

    var rowIdx = table.rows( nextRowId ).index();
    
    localEditor.inline(rowIdx, 'item_number' );
    

    Allan

  • cudzich09cudzich09 Posts: 12Questions: 2Answers: 1
    edited July 2018

    Thanks, so that clears up one of my mistakes, passing in an id instead of the index.
    I obtain the next row index in this fashion:

    var editRowIdx = rowsCurrentOrder.indexOf( data.DT_RowId );
    nextRowIdx = editRowIdx + 1;
    

    Calling the below line triggers an error of table rows index is not a function.
    var rowIdx = table.rows( nextRowId ).index();

    I have a local instance and ajax instance of the editor.
    Editor is already functioning correctly, it's activating inline mode on next row that is still giving me error.

    localEditor.inline(rowIdx, 'item_number' );
    executing the above line leaves me with Uncaught TypeError: Cannot read property 'contents' of undefined

    Still stumped, can I send you a link to private test case? @allan

    Thank you for your time.

  • cudzich09cudzich09 Posts: 12Questions: 2Answers: 1

    This line is triggering the exception:

        // Remove from DOM, keeping event handlers, and include text nodes in remove
        var children = node.contents().detach();
    

    Uncaught TypeError: Cannot read property 'contents' of undefined
    at Editor.inline (dataTables.editor.js:3061)
    at Editor.<anonymous> (queue:146)
    at Editor.dispatch (jquery.min.js:3)
    at Editor.q.handle (jquery.min.js:3)
    at Object.trigger (jquery.min.js:4)
    at r.fn.init.triggerHandler (jquery.min.js:4)
    at Editor._event (dataTables.editor.js:4932)
    at Editor._submitSuccess (dataTables.editor.js:5787)
    at dataTables.editor.js:5595
    at Object.opts.complete (dataTables.editor.js:4419)

  • cudzich09cudzich09 Posts: 12Questions: 2Answers: 1

    Alright!

    So, I have figured out how to move the focus to next line after triggering ajaxEditor, effectively accomplishing writing new data to the server.

    This line did the trick:
    table.cell( nextRowIdx, 1).focus();

    Cell focus and inline editor is activated with the above line without breaking any functionality on the site however an exception is still thrown.

    Uncaught TypeError: Cannot read property 'stopPropagation' of null
    at n._editor (datatables.min.js:566)
    at HTMLTableElement.<anonymous> (datatables.min.js:562)
    at HTMLTableElement.dispatch (jquery.min.js:3)
    at HTMLTableElement.q.handle (jquery.min.js:3)
    at Object.trigger (jquery.min.js:4)
    at r.fn.init.triggerHandler (jquery.min.js:4)
    at s.<anonymous> (datatables.min.js:567)
    at s.iterator (datatables.min.js:168)
    at n._emitEvent (datatables.min.js:567)
    at n._focus (datatables.min.js:569)

  • allanallan Posts: 63,455Questions: 1Answers: 10,465 Site admin

    var rowIdx = table.rows( nextRowId ).index();

    Should be:

    var rowIdx = table.row( nextRowId ).index();
    

    note the singular row() rather than rows() (which provides rows().indexes().

    If you could send me a test case showing the stopPropagation issue that would be great. Click my forum name above and then send message.

    Thanks,
    Allan

This discussion has been closed.