How to change bubble-edit to insert, after row created with rows.add()

How to change bubble-edit to insert, after row created with rows.add()

rpmccormickrpmccormick Posts: 107Questions: 13Answers: 0

So, I have a list of Guests. I know the list is, say, 3 guests long.
My database only has 1 guest, as the other 2 have not been added yet (don't want to create dummy's in DB when I find out total is 3).

I load my DataTable from the Guest list containing one row, then I use rows.add() to create the fake data for the 2 missing rows and display to the user as RealGuest, Guest #2, Guest #3.

Now, the user can bubble-edit the first row just fine. I want them to be able to bubble edit rows 2 and 3 also (both having GuestID=0, so both being row_0 possibly, which may be an issue I just realized). When they bubble edit an added row for the first time, then the row will be created with all the dummy data and their one real-data edit from the bubble.

Things I have tried:
preSubmit - Fires all the time, but has strange random fields none of them being Guest ID
preEdit - Fires for exsiting DB rows, doesn't even fire for the added rows, but editor json still sent.

Editor JSON always action='edit', always includes edited field and some other random fields with blanks (why?)

This is what I thought would work, and I still think would work... if preEdit was triggered for the added rows.

editor.on('preEdit', function (e, json, data, id) {
if (id==0) {e.mode('create');} //is it really this easy?
} );

In general, after using rows.add() and giving a datatable extra rows... how would one then update the DB using editor? I have searched but found no answer. Thanks in advance for your always awesome support.

This question has an accepted answers - jump to answer

Answers

  • rpmccormickrpmccormick Posts: 107Questions: 13Answers: 0

    NOTE: This is what I mean by random fields:

    AI-TITLE: ""
    DD-TITLE: ""
    Guest_BirthDate: ""
    Guest_Created: ""
    Guest_DisplayName: "hjgj"
    Guest_Modified: ""
    Guest_Status: "New"
    OD-TITLE: ""
    TOP-TITLE: ""

    ...there are many more fields, and all fields are sent to editor always. DisplayName was edited, and I guess all the TITLE's show up there, but why BirthDate (which is blank) and why Created/Modified (which should not be blank for the existing real row, but are in the preSubmit along with missing a ton including Guest_ID)

    NOTE 2: None of the above is important, just find me any way at all to intercept an Editor bubble-edit, and first create a row if it doesn't exists and then perform the bubble edit on the newly created ID (tricky?). I can use any ID's/data you like in creating the fake-rows using rows().add(), passing all the fields, same as the JSON that ran before it. Maybe I need to use Guest_ID = lastRealID+1 for the fake one ahead of time, as well as lastResID+2, instead of using Guest_ID=0 for both (Guest_ID is an auto-increment field, however, you can add a guest to an old reservation so they would have a much higher ID when created, so that wouldn't work)

  • rpmccormickrpmccormick Posts: 107Questions: 13Answers: 0

    NEW EXPERIMENT: I found the random fields drop off when I unlink the table (removed table: from editor create). The title plugin fields remain, as well as the one field edited. Note: I don't need the table linked, as the table data actually comes from a view of many tables, and each bubble edit is declared calling the appropriate editor and passing it ALL the fields for that particular table. A side effect is without it linked it I need to manually update the table, but worth it to have multiple editors with multiple-field bubbles in different columns across multiple DB tables that make up the view.

    Anyway, the above is not a solution, because Guest_ID is still not a field in preSubmit, and preEdit doesn't fire at all, not even for existing rows with a valid Guest_ID, when the table: is not used in the editor creation.

  • allanallan Posts: 61,650Questions: 1Answers: 10,093 Site admin

    Could you give me a link to your page so I can walk through it please? Certailny having overlapping IDs is going to be a problem.

    None of the above is important, just find me any way at all to intercept an Editor bubble-edit, and first create a row if it doesn't exists and then perform the bubble edit on the newly created ID (tricky?)

    Bubble edit should only be possible on rows which already exist. It can't be used to create new rows. You've created rows using rows.add() which is fine, that are "real" rows and should be editable as normal.

    Thanks,
    Allan

  • rpmccormickrpmccormick Posts: 107Questions: 13Answers: 0

    Hey Allan, thanks for the reply.

    While datatable.rows().add() does add a "real" row to DataTables, it does not make any AJAX call to update the database's real rows.

    I'm trying to use Bubble edit to create a row, in the same manner that you use Edit to duplicate a row in one of your examples (you just set e.mode('create'); and it is magic).

    My goal is to add "fake" rows to the DataTable (already works) without updating the database (it already doesn't) and then if an field of the row is edited (with bubble editor, or full editor, or inline, or whatever) I want to to insert a row, using all the "fake" row data that was provided in rows().add(), plus the update to whatever field was edited.

    I will PM you with login information to my site so you can see it in person.

  • rpmccormickrpmccormick Posts: 107Questions: 13Answers: 0

    DataTable PHP code:

          $sql = 'SELECT * FROM FrontDesk_Popups_ResGuests WHERE HotelReservation_ID='.$resid.' AND Hotel_ID='.$hotelid;
          include HOST_API.'/dt/getTable.php';
    
    

    ...where FrontDesk_Popups_ResGuests is a VIEW containing many complex table joins, but the primary table is Hotel_Guests

    Editor PHP code:

          //Main Table
          $tablename = 'Hotel_Guests';
          $tablecode = 'Guest_';
          $Editor = Editor::inst($db, $tablename, $tablecode.'ID')
             ->field(Field::inst($tablecode.'ID')       ->set(false),
                     Field::inst($tablecode.'Created')  ->set(false) ->getFormatter('Format::date_sql_to_format', $DateFormat),
                     Field::inst($tablecode.'Modified') ->set(false) ->getFormatter('Format::date_sql_to_format', $DateFormat),
                     Field::inst($tablecode.'DisplayName')   ),
                     Field::inst($tablecode.'Status')        ),
                     Field::inst($tablecode.'PrimaryID'      ),
                     Field::inst($tablecode.'PrimaryRelation'),
                     Field::inst($tablecode.'Notes'          ),
                     Field::inst($tablecode.'Type'           ),
                     Field::inst($tablecode.'Race'           ),
                     Field::inst($tablecode.'Gender'         ),
                     Field::inst($tablecode.'Age'            ),
                     Field::inst($tablecode.'AgeBucket'      ),
                     Field::inst($tablecode.'BirthDate'      ),
                     Field::inst('ContactInfo_ID')           ),
                     Field::inst('Hotel_ID')
                    );
           
          // Run it
          $Editor->process($_POST)->json();
    

    Bubble-Edit code:

          render = '<b>'+fullname+'</b><br><small style="color:#888;">'+relation+'</small>';
          
          //EDITOR Fields
          edLink = 'ResGuestEditor.bubble(this, [\'Guest_DisplayName\', \'Guest_PrimaryRelation\']);';
          edLink = ' data-editor-id="'+GR.Guest_ID+'" onclick="'+edLink+'"';
          render = '<div'+edLink+'>'+render+                                                  //must include all fields
                     '<span style="display:none;" data-editor-field="Guest_ID">'             +GR.Guest_ID             +'</span>'+
                     '<span style="display:none;" data-editor-field="Guest_DisplayName">'    +GR.Guest_DisplayName    +'</span>'+
                     '<span style="display:none;" data-editor-field="Guest_AgeBucket">'      +GR.Guest_AgeBucket      +'</span>'+
                     '<span style="display:none;" data-editor-field="Guest_Gender">'         +GR.Guest_Gender         +'</span>'+
                     '<span style="display:none;" data-editor-field="Guest_Status">'         +GR.Guest_Status         +'</span>'+
                     '<span style="display:none;" data-editor-field="Guest_Type">'           +GR.Guest_Type           +'</span>'+
                     '<span style="display:none;" data-editor-field="Guest_Race">'           +GR.Guest_Race           +'</span>'+
                     '<span style="display:none;" data-editor-field="Guest_Age">'            +GR.Guest_Age            +'</span>'+
                     '<span style="display:none;" data-editor-field="Guest_BirthDate">'      +GR.Guest_BirthDate      +'</span>'+
                     '<span style="display:none;" data-editor-field="Guest_PrimaryID">'      +GR.Guest_PrimaryID      +'</span>'+
                     '<span style="display:none;" data-editor-field="Guest_PrimaryRelation">'+GR.Guest_PrimaryRelation+'</span>'+
                     '<span style="display:none;" data-editor-field="Guest_Status">'         +GR.Guest_Status         +'</span>'+
                     '<span style="display:none;" data-editor-field="Guest_Notes">'          +GR.Guest_Notes          +'</span>'+
                     '<span style="display:none;" data-editor-field="Hotel_ID">'             +GR.Hotel_ID             +'</span>'+
                     '<span style="display:none;" data-editor-field="ContactInfo_ID">'       +GR.ContactInfo_ID       +'</span>'+
                     '<span style="display:none;" data-editor-field="Guest_Created">'        +GR.Guest_Created        +'</span>'+
                     '<span style="display:none;" data-editor-field="Guest_Modified">'       +GR.Guest_Modified       +'</span>'+
                   '</div>';
    
          return render;
    
  • rpmccormickrpmccormick Posts: 107Questions: 13Answers: 0

    Might as well post the final parts too...

    DataTable:

    <script>
      //--- START ResGuests DATATABLE --------------------------------------------\\
      $('#tblQuoteGuests').dataTable(
       {responsive: {orthogonal: 'responsive'},
       deferRender: true,
               dom: 'tB',
             order: [3, 'desc'],
            paging: false,
          ordering: false,
           buttons: [<?php include HOST_SRC.'/dt/exportButtons.php' ?>],   
           columns: [{className: 'control'  , defaultContent: ''          , orderable: false},
                     {className: 'dt-center', render: renderGuestIcon     , orderable: false},
                     {className: 'dt-center', render: renderGuestName     , orderable: false},
                     {className: 'dt-center', render: renderGuestType     , orderable: false},
                     {className: 'dt-center', render: renderGuestRoom     , orderable: false},
                     {className: 'dt-center', render: renderGuestDBDetails, orderable: false},
                     {className: 'dt-center', render: renderGuestActions  , orderable: false},
                    ]
       }                     );
      //--- END ResGuests DATATABLE ----------------------------------------------*/
    </script>
    

    Editor:

    <?php
      $edt_name = 'ResGuestEditor';
      $tbl_name = '#tblQuoteGuests';
      $tbl_key  = 'Guest_ID';
      $api_path = '/api/dt/popups/edResGuest';
    
      $StatusOpts    = 'options: [{label: "New Guest (Quote)"      , value: "New"      },'.
                                 '{label: "Returning Guest"        , value: "Returning"},'.
                                 '{label: "Upcoming Guest"         , value: "Upcoming" },'.
                                 '{label: "First Time Guest"       , value: "First"    },'.
                                 '{label: "VIP Guest"              , value: "VIP"      },'.
                                 '{label: "Past Guest"             , value: "Past"     }]';
    
      $GenderOpts    = 'options: [{label: "Male (Man/Boy)"         , value: "M"        },'.
                                 '{label: "Female (Woman/Girl)"    , value: "F"        },'.
                                 '{label: "Non-Binary"             , value: "Upcoming" },'.
                                 '{label: "Decline to Say"         , value: ""         }]';
    
      $AgeBucketOpts = 'options: [{label: "Adult"                  , value: "Adult"    },'.
                                 '{label: "Child"                  , value: "Child"    },'.
                                 '{label: "Newborn (age in months)", value: "Newborn"  },'.
                                 '{label: "Senior"                 , value: "Senior"   },'.
                                 '{label: "Pet"                    , value: "Pet"      }]';
    
      $bs = ', attr: {"class": "form-control"}';
    ?>
    
    <script>
      //--- START EDITOR ------------------------------------------------\\
      <?=$edt_name?> = new $.fn.dataTable.Editor(
       {ajax: '<?=$api_path?>',
    //   table: '<?=$tbl_name?>',
       idSrc: '<?=$tbl_key?>',
      fields: [{label: 'Guest Information'      , name: 'TOP-TITLE'                    , type: 'title'  },
                 {label: 'Display Name'         , name: 'Guest_DisplayName'    <?=$bs?>, type: 'text'   },
                 {label: 'Age Bucket'           , name: 'Guest_AgeBucket'      <?=$bs?>, type: 'select', <?=$AgeBucketOpts?>},
                 {label: 'Gender'               , name: 'Guest_Gender'         <?=$bs?>, type: 'select', <?=$GenderOpts?>},
                 {label: 'Status'               , name: 'Guest_Status'         <?=$bs?>, type: 'select', <?=$StatusOpts?>},
               {label: 'Additional Information' , name: 'AI-TITLE'                     , type: 'title'  },
                 {label: 'Primary ID'           , name: 'Guest_PrimaryID'      <?=$bs?>, type: 'text'   },
                 {label: 'Relation to Primary'  , name: 'Guest_PrimaryRelation'<?=$bs?>, type: 'text'   },
                 {label: 'Notes'                , name: 'Guest_Notes'          <?=$bs?>, type: 'text'   },
               {label: 'Optional Details'       , name: 'OD-TITLE'                     , type: 'title'  },
                 {label:'Custom Top Text (Pet Type, Dog/Cat)',name:'Guest_Type'<?=$bs?>, type: 'text'   },
                 {label:'Custom Bottom Text (ie:  Pet Breed)',name:'Guest_Race'<?=$bs?>, type: 'text'   },
                 {label:'Age (in months for Newborn, 0=unknown)',name:'Guest_Age'<?=$bs?>,type:'text'   },
                 {label:'Birth-Date (will update age yearly)',name:'Guest_BirthDate'<?=$bs?>,type:'text'},
               {label: 'Database Details'       , name: 'DD-TITLE'                     , type: 'title'  },
                 {label: 'Hotel ID'             , name: 'Hotel_ID'             <?=$bs?>, type: 'display'}, 
                 {label: 'Guest ID'             , name: 'Guest_ID'                     , type: 'display'}, 
                 {label: 'Contact ID'           , name: 'ContactInfo_ID'               , type: 'display'}, 
                 {label: 'Created'              , name: 'Guest_Created'                , type: 'display'}, 
                 {label: 'Modified'             , name: 'Guest_Modified'               , type: 'display'}
              ]
       }                                        );
      //--- END EDITOR --------------------------------------------------*/
    
    //preEdit not called for non-existing rows, but edResGuest still called...  why?  And with strange variables too!
      <?=$edt_name?>.on('preEdit', function (e, json, data, id)
       {//Do stuff to each field
        //like convert dates and stuff
        console.log('PREEDIT---> Guest ID: '+data['Guest_ID'], e, json, data, id);
        //note, json.data is data, and it contains the id too, but whatever
        if (id==0) {e.mode('create');} //is it really this easy?
       }               );
    
      <?=$edt_name?>.on('preSubmit', function (e, data, action) 
       {$.each(data.data, function (key, values) 
         {//Do stuff to each field
          //data.data[key]['numberField'] = parseInt(values['numberField'], 10);
          //data.data[key]['dates'] = make dates right!
          console.log('PRESUBMIT---> Guest ID: '+data.data[key]['Guest_ID'], data.data[key]); //Only has Created, Modified, and Titles... Why?
          //if (data.data[key]['GuestID']<1)
          // {console.log('mode create?', e);} else {console.log('no mode create?', e);}
          //e.mode('create');
         }
              );
       }               );
    
    </script>
    
  • rpmccormickrpmccormick Posts: 107Questions: 13Answers: 0

    I should have posted the entire render function above. Here it is now:

        //--------------------------------------------------------------
        function renderGuestName(row, type, data)
         {let GR = data;
          let render = GR.Guest_DisplayName+GR.Guest_Status;
          if ((type=='sort')||(type=='type')) {return render;}
          let  fullname = GR.Guest_DisplayName;
          let  relation = GR.Guest_PrimaryRelation;
          if ((relation == 'self') || (relation == 'Self')) {relation = 'Primary Guest';}
    
          render = '<b>'+fullname+'</b><br><small style="color:#888;">'+relation+'</small>';
          
          //EDITOR Fields
          edLink = 'ResGuestEditor.bubble(this, [\'Guest_DisplayName\', \'Guest_PrimaryRelation\']);';
          edLink = ' data-editor-id="'+GR.Guest_ID+'" onclick="'+edLink+'"';
          render = '<div'+edLink+'>'+render+                                                  //must include all fields
                     '<span style="display:none;" data-editor-field="Guest_ID">'             +GR.Guest_ID             +'</span>'+
                     '<span style="display:none;" data-editor-field="Guest_DisplayName">'    +GR.Guest_DisplayName    +'</span>'+
                     '<span style="display:none;" data-editor-field="Guest_AgeBucket">'      +GR.Guest_AgeBucket      +'</span>'+
                     '<span style="display:none;" data-editor-field="Guest_Gender">'         +GR.Guest_Gender         +'</span>'+
                     '<span style="display:none;" data-editor-field="Guest_Status">'         +GR.Guest_Status         +'</span>'+
                     '<span style="display:none;" data-editor-field="Guest_Type">'           +GR.Guest_Type           +'</span>'+
                     '<span style="display:none;" data-editor-field="Guest_Race">'           +GR.Guest_Race           +'</span>'+
                     '<span style="display:none;" data-editor-field="Guest_Age">'            +GR.Guest_Age            +'</span>'+
                     '<span style="display:none;" data-editor-field="Guest_BirthDate">'      +GR.Guest_BirthDate      +'</span>'+
                     '<span style="display:none;" data-editor-field="Guest_PrimaryID">'      +GR.Guest_PrimaryID      +'</span>'+
                     '<span style="display:none;" data-editor-field="Guest_PrimaryRelation">'+GR.Guest_PrimaryRelation+'</span>'+
                     '<span style="display:none;" data-editor-field="Guest_Status">'         +GR.Guest_Status         +'</span>'+
                     '<span style="display:none;" data-editor-field="Guest_Notes">'          +GR.Guest_Notes          +'</span>'+
                     '<span style="display:none;" data-editor-field="Hotel_ID">'             +GR.Hotel_ID             +'</span>'+
                     '<span style="display:none;" data-editor-field="ContactInfo_ID">'       +GR.ContactInfo_ID       +'</span>'+
                     '<span style="display:none;" data-editor-field="Guest_Created">'        +GR.Guest_Created        +'</span>'+
                     '<span style="display:none;" data-editor-field="Guest_Modified">'       +GR.Guest_Modified       +'</span>'+
                   '</div>';
    
          return render;
         }
        //--------------------------------------------------------------
    
  • allanallan Posts: 61,650Questions: 1Answers: 10,093 Site admin

    Many thanks - I've sent a reply via PM :)

    Allan

  • rpmccormickrpmccormick Posts: 107Questions: 13Answers: 0

    I just wanted to follow up on my post. FYI: I was able to get it to work, using bubble edit with mode="create". If anyone needs help with that, feel free to PM me.

    I do have one last issue... I need to updater the Editor AJAX, but can't get it to work. Anyone got any ideas?

    The issue is the ResID can change without reloading the page, and I need to send ResID to Editor even though it is not part of the table (it is for a secondary lookup table that links Guests to Reservations)

    I have tried something like this, but no luck yet:

      ResGuestEditor.on('preSubmit', function (e, request)
       {this.ajax().url = '<?=$api_path?>?res='+$('#ResQuoteID').val(); //no work?
        //let _url = new URL(window.location.origin + "/" + editor.ajax().url)
        //this.ajax().url  = `${_url.protocol}//${_url.host}/api/v1/some-endpoint/${Object.keys(request.data)[0]}/${_url.search}`;
        //this.ajax().data = request.data[Object.keys(request.data)];
        //https://stackoverflow.com/questions/68944431/change-the-edit-url-dynamically-using-the-datatable-editor
       }
    
  • allanallan Posts: 61,650Questions: 1Answers: 10,093 Site admin
    Answer ✓

    I've just sent a reply by PM - sorry for the delay there. For completeness, I've copied my response here:

    ResGuestEditor.ajax().url = '/api/dt/popups/edResGuest?res=892';

    That isn't working because your original ajax option for Editor doesn't have a url property - it is actually a string itself:

    ajax: '/api/dt/popups/edResGuest?res=101'
    

    So ajax() is just returning that original string and then the .url is adding a new property to it!

    What to do here is for the initialisation have:

    ajax: {
      url: '/api/dt/popups/edResGuest?res=101'
    }
    

    then your line of code will work.

    Even better I would say though is to use ajax.data as a function and append the data to the query being sent to the server:

    ajax: {
      url: '/api/dt/popups/edResGuest',
      data: function (d) {
        d.res = '101'; // change this to be a variable or whatever to get the value you want to send.
      }
    }
    

    Note that submits in the query body not the query parameters.

    Allan

  • rpmccormickrpmccormick Posts: 107Questions: 13Answers: 0

    Great. I will use ajax with the url and data-function parameters.

    One question... how do I then access it in the PHP lib?
    ...replace this with what?

    $resid = $_REQUEST['res']

  • rpmccormickrpmccormick Posts: 107Questions: 13Answers: 0

    Nevermind, I see in your PM you said $_POST should get it (so $_REQUEST should probably still work too).

    Also per your PM, I will try enabling bubble to submit all fields instead of only changed fields, and then I will be able to get the data I need from that instead of "s"

    I will report back with all the final code once this is done, in case it ever helps anyone else.

Sign In or Register to comment.