Standalone Editor - Save and Delete buttons on same form?

Standalone Editor - Save and Delete buttons on same form?

weedenbweedenb Posts: 25Questions: 6Answers: 0

I have a standard datatables/editor page representing a primary table in which I'm using rendered buttons to call up a separate standalone editor for editing records in a secondary table . This is a complete standalone record object with no DOM representation in the page and is only carried in a local javascript variable. After looking at the examples and reading the documentation, putting it together was pretty straightforward. The following works just as I wanted and will either pop up a create editor screen if no matching record exists or an edit screen if one does.

The one feature I can't seem to resolve is how to incorporate a 'delete record' button within the 'edit record' screen? Pre-determining whether a user wants to edit or delete a record is way beyond my limited javascript skills. Using the buttons API I can get lots of more buttons on the form but I can't seem to get .remove(id) to work from within an .edit(id) form without breaking something (usually back at Editor.php on the server side). I have noticed the record(s) object is slightly different between update and delete action where delete includes DT_RowId and update doesn't? Not sure if I have simply structured my way into a corner or am missing something in my record object??

On a side note, using an async ajax call seemed to cause complications, I understand my synchronous call is frowned on but there is absolutely nothing the user or the browser could be doing in the mean time here until the server responds back. Do I fail on principle or get a pass?

Ps. Fantastic tool! only been using it for a couple of weeks and love it :)

  $('#turnover').on( 'click', 'button.bolt', function () {
    ShiftHid = $(this)[0].id;
    var record;
    $.ajax( {
      url: 'php/bolt.php?hid='+ShiftHid,
      async: false,
      dataType: 'json',
      success: function ( json ) {
        record = json.data[0];
      }
    } );
    if (record) {
      var id = record.DT_RowId;
      delete record.DT_RowId; //had to do this to make edit happy
      bolteditor
      .title('Edit record')
      .buttons('Save Record') //would like a second 'Delete Record' button also?
      .edit( id, false )
      .set( record )
      .open();
    }
    else {
      bolteditor
      .title('Create new record')
      .buttons('Create')
      .create(false)
      .val('hid', ShiftHid)
      .open();
    }
  } );

Replies

  • allanallan Posts: 61,765Questions: 1Answers: 10,111 Site admin

    The one thing I would say about the use of async: false is that asynchronous requests have been deprecated in the XMLHttpRequest and might potentially be removed in future. I'd suggest just dropping the if code blocks into your success callback - I don't see any benefit of having it synchronous there.

    Regarding the real issue - can you show me what you tried with the remove() call?

    Finally for now, the data submitted from edit is slightly different from delete since delete submits the original data object from the row, not the values form the editing form (since there is no editing form required when deleting an entry).

    Regards,
    Allan

  • weedenbweedenb Posts: 25Questions: 6Answers: 0

    Thanks Allan, simply moving everything into the ajax callback solved the async issue.

    Here is one of my attempts at the edit/delete functionality. Save changes and Cancel work fine, can't seem to get my head around the remove part. Have confirmed the form action:"remove" is present in the ajax call but with no id parameter present.

      $('#turnover').on( 'click', 'button.bolt', function () {
        ShiftHid = $(this)[0].id;
        var record;
        $.ajax( {
          url: 'php/bolt.php?hid='+ShiftHid,
    //    async: false,
          dataType: 'json',
          success: function ( json ) {
            record = json.data[0];
            if (record) {
              var id = record.DT_RowId;
              delete record.DT_RowId;
              bolteditor
              .title('Edit record')
              .buttons([
                {
                  label: 'Save Changes',
                  fn: function () {
                    this.submit();
                  }
                },
                {
                  label: 'Close',
                  fn: function () {
                    this.close();
                  }
                },
                {
                  label: 'Delete Record',
                  fn: function () {
                    alert( 'id = '+id); // I'm getting the right id here = row_xxx
                    this.remove(id,false) // This without a submit doesn't do anything
                    this.submit() // with submit throws error "No ids submitted for the delete","data":[] in Editor.php
                    this.close(); // without close, falls back into editor form
                  }
                }
              ])
              .edit( id, false )
              .set( record )
              .open();
            }
            else {
              bolteditor
              .title('Create new record')
              .buttons('Create')
              .create(false)
              .val('hid', ShiftHid)
              .open();
            }
          }
        } );
      } );
    
  • weedenbweedenb Posts: 25Questions: 6Answers: 0
    edited December 2015

    Here is another attempt which defines my problem a bit simpler without the buttons complications. I don't seem to have the data source object properly defined for a standalone remove operation? As you and the Client/server documentation both note:

    "On remove the original data source object for the row is submitted as the data object."

    Create and edit operations appear to function fine with the provided data but my remove is still missing something that a normal DOM based editor instance provides?

      $('#turnover').on( 'click', 'button.bolt', function () {
        ShiftHid = $(this)[0].id;
        var record;
        $.ajax( {
          url: 'php/bolt.php?hid='+ShiftHid,
          dataType: 'json',
          success: function ( json ) {
            record = json.data[0];
            if (record) {
             var id = record.DT_RowId;
    //        delete record.DT_RowId; //need this for edit to work
              bolteditor
              .title('Delete record')
              .buttons('Delete')
              .message('Are you sure you wish to delete this record?')
              .remove( id) ; // throws error: TypeError: c[a] is undefined in datatables.js
            }
            else {
              bolteditor
              .title('Create new record')
              .buttons('Create')
              .create(false)
              .val('hid', ShiftHid)
              .open();
            }
          }
        } );
      } );
    
  • allanallan Posts: 61,765Questions: 1Answers: 10,111 Site admin

    Could you try:

    {
      label: 'Delete Record',
      fn: function () {
        this
          .remove( this.modifier(), false )
          .submit();
      }
    }
    

    I don't think close() should be required - the submit success should do that automatically and probably isn't due to the error at the moment.

    modifier() gets the id that was used to trigger the original edit, so I think that should just improve the reliability of that call.

    Allan

  • weedenbweedenb Posts: 25Questions: 6Answers: 0

    Thanks Allan, unfortunately still throws the following error for remove:

    Warning: Invalid argument supplied for foreach() in /var/www/html/heading/php/lib/Editor/Editor.php on line 906
    {"error":"No ids submitted for the delete","data":[]}

    Here is a copy of the s: object from a console.log(this) at the remove attempt. A successful edit call object looks very much the same except the action: is null. (not sure the best way to post objects into this forum but I can expand any of these if needed?). I'm assuming the id:-1 is probably not what I need here?

    s: Object
    action: "remove"
    ajax: "php/bolt.php"
    ajaxUrl: null
    closeCb: ()
    closeIcb: ()
    dataSource: Object
    dbTable: null
    displayController: Object
    displayed: "main"
    domTable: null
    editCount: 1
    editData: Object
    editFields: Object
    editOpts: Object
    fields: Object
    formOptions: Object
    id: -1
    idSrc: "DT_RowId"
    includeFields: Array[13]
    legacyAjax: false
    modifier: "row_496"
    opts: null
    order: Array[13]
    processing: false
    setFocus: f.Field
    table: null
    __proto__: Object
    

    I'm slowly coming to understanding of how all this works under the covers but have a ways to go yet. I can see how create just needs a defined editor instance to assemble a new record from nothing and be sent off to the db. An edit record gets processed through an editor form so it gets all put into order internally before being sent off. Remove as you noted doesn't need to pass through a edit form? This is where I'm still lost as I'm not seeing how my external data object (record) gets associated into the editor object (or the "original data source" reference)? For edit I can load up the field values with the set() method, how does remove know what I'm trying to do with what?

    Thanks Again!

  • allanallan Posts: 61,765Questions: 1Answers: 10,111 Site admin

    Are you able to give me a link to the page? I've just tried using this:

                {
                    extend: "edit",
                    editor: editor,
                    formButtons: [
                        {
                            label: 'Delete Record',
                            fn: function () {
                                this
                                    .remove( this.modifier(), false )
                                    .submit();
                            }
                        },
                        'Submit'
                    ]
                },
    

    in this example and it does seem to work as expected (the row is deleted).

    I don't understand why that wouldn't be working on your own page I'm afraid.

    Thanks,
    Allan

  • weedenbweedenb Posts: 25Questions: 6Answers: 0

    Unfortunately I can't provide an accessible link. I'll see if I can work something into http://live.datatables.net/ . I'll need an accessible data source to demonstrate. Are any of the demo datasources available here?

    Your solution works perfectly when working with an editor instance that has a table reference. My Standalone editor instance has no table reference and my data object has no DOM representation. All I've got is my lonely little javascript variable that doesn't know how to join the Datatables party?

  • weedenbweedenb Posts: 25Questions: 6Answers: 0
    edited December 2015

    Couldn't figure out how to live.datable or jsfiddle the db sources so I modified one of the examples ( https://editor.datatables.net/examples/simple/join.html) as a starting point. This uses the users and sites tables and the join.php script from the examples. The result is pretty useless but it illustrates what I'm trying to do. The source paths will need to be changed of course.

    Html source:

    <!doctype html>
    <html>
        <head>
            <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    
            <title>Test Standalone</title>
    
            <link rel="stylesheet" type="text/css" href="DataTables/datatables.min.css"/>
    
            <script type="text/javascript" src="DataTables/datatables.min.js"></script>
            <script type="text/javascript" charset="utf-8" src="lonely.js"></script>
    
        </head>
        <body class="dataTables">
            <div class="container">
    
            <div><H1>Test Standalone</H1></div>
    
            <table id="example" class="display" cellspacing="0" width="100%">
                    <thead>
                        <tr>
                            <th>First name</th>
                            <th>Last name</th>
                            <th>Phone #</th>
                            <th>Location</th>
                            <th>Site Edit</th>
                        </tr>
                    </thead>
                </table>
            </div>
        </body>
    </html>
    

    Javascript:

    // lonely.js
    var editor; // main editor for the users/sites join 
    var edit_site; // Standalone Editor for sites
     
    $(document).ready(function() {
        editor = new $.fn.dataTable.Editor( { // Main editor
          ajax: "php/join.php",
          table: "#example",
          fields: [ {
            label: "First name:",
            name: "users.first_name"
          }, {
            label: "Last name:",
            name: "users.last_name"
          }, {
            label: "Phone #:",
            name: "users.phone"
          }, {
            label: "Site:",
            name: "users.site",
            type: "select",
            placeholder: "Select a location"
          }
                  ]
        } );
    
        edit_site = new $.fn.dataTable.Editor( { // Standalone editor, Note: no table reference
            ajax: "php/sites.php",
            fields: [ {
                    label: "Name:",
                    name: "name"
                }
            ]
        } );
     
      $('#example').on( 'click', 'button.site', function () {
        sitename = $(this)[0].id;
        var record;
        $.ajax( {
          url: encodeURI('php/sites.php?name='+sitename),
          dataType: 'json',
          success: function ( json ) {
            var record = json.data[0];    // could get multiple records but just grab the first
            if (record) {                 // if no existing record then else to create
              var id = record.DT_RowId;
              delete record.DT_RowId;     // need to strip DT_RowId out before using to set current values for .edit
              edit_site
              .title('Modify Site record')
              .buttons([
                {
                  label: 'Save Changes',
                  fn: function () {
                     this.submit();
                  }
                },
                {
                  label: 'Close',
                  fn: function () {
                    this.close();
                  }
                },
                {
                  label: 'Delete Record',
                  fn: function () {
                    this
                    .remove( this.modifier(), false )
                    .submit();   // *Breaks the server script here
                  }
                }
              ])
              .edit( id, false )
              .set( record )     //load any existing field values from query return
              .open();
            }
            else {
              edit_site
              .title('Create new record')
              .buttons('Create')
              .create(false)
              .open();
            }
          }
        } );
      } );
      
      $('#example').DataTable( { // Main datatable 
            dom: "Bfrtip",
            ajax: {
                url: "php/join.php",
                type: 'POST'
            },
            columnDefs: [
              { "targets": 4,
                "data": null,
                "render": function ( data,  row ) {
                  var buttons ='<button class=site id='+JSON.stringify(data.sites.name)+'>'+data.sites.name+'</button>';
                  return buttons;
                }
              }
            ],
            columns: [
                { data: "users.first_name" },
                { data: "users.last_name" },
                { data: "users.phone" },
                { data: "sites.name" }
            ],
            select: true,
            buttons: [
                { extend: "create", editor: editor },
                { extend: "edit",   editor: editor },
                { extend: "remove", editor: editor }
            ]
        } );
    } );
    

    Needed one additional server script to access the sites table. This one has a bit of a hack with a selective "where" statement used to retrieve a single record. Not very pretty with a GET request and POST response but it appears to work fine for all normal CRUD operations.

    <?php
    
    // sites.php
    // DataTables PHP library
    include( "lib/DataTables.php" );
    
    // Alias Editor classes so they are easy to use
    use
        DataTables\Editor,
        DataTables\Editor\Field,
        DataTables\Editor\Format,
        DataTables\Editor\Join,
        DataTables\Editor\Upload,
        DataTables\Editor\Validate;
    
    
    /*
     * Example PHP implementation used for the join.html example
     */
    $editor = Editor::inst( $db, 'sites')
        ->field( 
            Field::inst( 'name' )
        );
    
    // bit of a hack here to get selected record(s) if wanted via GET parameter
    // should form a proper ajax post request instead
    
    if ( isset($_GET['name']) ) {  
         $editor->where( 'name', $_GET['name'] );
    }
    
    $editor
    
        ->process($_POST)
        ->json();
    
  • allanallan Posts: 61,765Questions: 1Answers: 10,111 Site admin

    Thanks for showing how to modify one of the existing examples to demonstrate this issue.

    The problem is coming from the fact that there is no data to submit for the name (in that specific example). This comes about from the fact that name is undefined since there is no element on the page that gives that value directly (indeed you use set() to set that value for the edit case.

    So we need to work around that since in order for the id to be submitted there must be some data. Either the HTML5 attributes can be used for the standalone Editor to give the field a value, or we can add the following immediately prior to the submit() call:

                    .on( 'preSubmit', function ( e, d ) {
                        d.data[ this.modifier() ] = '';
                    } )
    

    That will give it data and thus allow the submit to occur.

    Allan

  • weedenbweedenb Posts: 25Questions: 6Answers: 0

    That did the trick Allan! Thank you very much!

This discussion has been closed.