Adding New related record without leaving the page

103 views
Skip to first unread message

Antonio Sottocasa

unread,
Jul 16, 2019, 12:58:22 PM7/16/19
to Xataface
he quickest way to implement this is the grid widget, but it isn't necesserily the best one, even if it's good for many cases.
In this scenario, we had several problems:
- The form becomes too long and cluttered, as the table already has a load of fields, field groups, and attachments.
- We need to create the record including the relation-bearing-field, and for some reason this resulted in the field being filled blank in both records upon save.

The scenario
The real life scenario was similar to the one described below:
While creating a new enquiry, quotation, invoice, etc. we needed to be able to create a new customer.
All of those tables have a relation to the customer field 'name', which has to become the field 'customer' in the table.
Given that the customer table has hundreds entries and growing, select or depselect are out of question, and we're using the brilliant yui_autocomplete feature, which works beautifully.

The solution:
I thought I might do something something AJAX something. The calendar module tells me that I won't have to write loads of code as all I need is already there. But where?
Reading the calendar_display.js I find the following function:
function addEvent(date, allDay){
 
var dlg = new xataface.RecordDialog({
 table
: tableName,
 callback
: function(data){
 
 $
(self).fullCalendar('refetchEvents');
 
 
},
 
params: {
 
//'--calendar-new-default-start-date': Math.floor(((date.getTime()/1000)+serverOffset-clientOffset)),
 
'--calendar-new-default-start-date': Math.floor(((date.getTime()/1000))),
 
'--calendar-new-default-all-day': allDay?1:0
 
 
}
 
 
});
 
 dlg
.display();
 
 
}

A quick google search for xataface.RecordDialog brings me here:

That's exactly what I need.

From this point onward it will be easy, even for someone who doesn't  speak javascript at all.


The recipe

1. Create a 'javascripts.js' file in the main dir of your application

2. Write there the RecordDialog function we need
//require <RecordDialog/RecordDialog.js>


function addCustomer(){

 
var dlg = new xataface.RecordDialog({
 table
: 'customers'
});
  dlg.display();
}



3. Open the delegate class for the table where we want the 'add Customer' button, in this case 'invoices'. Our delegate class 'invoices.php' will look like:

<?php


class tables_invoices {
   
function block__custom_javascripts(){
    echo
'<script src="/myapp/javascripts.js" type="text/javascript" language="javascript"></script>'; // change 'myapp' with your app dir
  echo
'<script src="../xataface/js/RecordDialog/RecordDialog.js" type="text/javascript" ></script>'; // my xataface dir is right outside my app dir, yours could be elsewhere
}


 
function block__after_customer_widget(){
        echo
'<span class="button" onclick="addCustomer()"><a href="#">New Customer</a></span>';
// the anchor a href is there just to give the pointer the proper shape when you over the button.
   
}


}

?>



4. Now we have a working button to add a new customer, but then we can't have our new customer in the autocomplete list without reloading the page, which we don't want to reload so we won't lose data already entered. We need to pass the new customer name onto the 'customer' field in the new invoice record.  H̶a̶v̶i̶n̶g̶ ̶t̶o̶ ̶p̶a̶s̶s̶ ̶a̶ ̶v̶a̶r̶i̶a̶b̶l̶e̶ ̶b̶e̶t̶w̶e̶e̶n̶ ̶J̶a̶v̶a̶s̶c̶r̶i̶p̶t̶ ̶a̶n̶d̶ ̶P̶H̶P̶ ̶I̶'̶l̶l̶ ̶w̶r̶i̶t̶e̶ ̶a̶ ̶c̶o̶o̶k̶i̶e̶ ̶(̶t̶h̶e̶ ̶h̶o̶r̶r̶o̶r̶.̶.̶.̶ ̶b̶u̶t̶ ̶n̶o̶n̶e̶t̶h̶e̶l̶e̶s̶s̶ ̶a̶ ̶w̶o̶r̶k̶i̶n̶g̶ ̶s̶o̶l̶u̶t̶i̶o̶n̶)̶.̶ ̶S̶o̶ ̶w̶e̶ ̶a̶d̶d̶ ̶t̶h̶i̶s̶ ̶c̶a̶l̶l̶b̶a̶c̶k̶ ̶f̶u̶n̶c̶t̶i̶o̶n̶ ̶p̶i̶e̶c̶e̶ ̶o̶f̶ ̶c̶o̶d̶e̶ ̶t̶o̶ ̶o̶u̶r̶ ̶a̶d̶d̶C̶u̶s̶t̶o̶m̶e̶r̶ ̶f̶u̶n̶c̶t̶i̶o̶n̶ ̶i̶n̶ ̶t̶h̶e̶ ̶j̶a̶v̶a̶s̶c̶r̶i̶p̶t̶s̶.̶j̶s̶ ̶f̶i̶l̶e̶.̶

5̶.̶ ̶N̶o̶w̶,̶ ̶w̶e̶ ̶n̶e̶e̶d̶ ̶t̶o̶ ̶p̶a̶s̶s̶ ̶t̶h̶e̶ ̶c̶o̶o̶k̶i̶e̶ ̶v̶a̶l̶u̶e̶ ̶t̶o̶ ̶t̶h̶e̶ ̶c̶u̶s̶t̶o̶m̶e̶r̶ ̶f̶i̶e̶l̶d̶ ̶i̶n̶ ̶o̶u̶r̶ ̶d̶e̶l̶e̶g̶a̶t̶e̶ ̶c̶l̶a̶s̶s̶.̶ ̶X̶a̶t̶a̶f̶a̶c̶e̶ ̶g̶i̶v̶e̶s̶ ̶u̶s̶ ̶t̶h̶e̶ ̶s̶e̶t̶V̶a̶l̶u̶e̶ ̶f̶u̶n̶c̶t̶i̶o̶n̶ ̶t̶o̶ ̶d̶o̶ ̶t̶h̶i̶s̶,̶ ̶w̶h̶i̶c̶h̶ ̶w̶e̶ ̶p̶u̶t̶ ̶i̶n̶ ̶t̶h̶e̶ ̶b̶e̶f̶o̶r̶e̶S̶a̶v̶e̶ ̶f̶u̶n̶c̶t̶i̶o̶n̶.̶
 ̶6̶.̶ ̶I̶'̶v̶e̶ ̶g̶o̶t̶ ̶y̶o̶u̶.̶ ̶T̶h̶i̶s̶ ̶w̶a̶y̶ ̶i̶n̶ ̶t̶h̶e̶ ̶n̶e̶x̶t̶ ̶n̶e̶w̶ ̶i̶n̶v̶o̶i̶c̶e̶ ̶r̶e̶c̶o̶r̶d̶ ̶w̶e̶'̶l̶l̶ ̶e̶n̶t̶e̶r̶ ̶t̶h̶e̶ ̶c̶u̶s̶t̶o̶m̶e̶r̶ ̶f̶i̶e̶l̶d̶ ̶a̶n̶d̶ ̶i̶t̶ ̶w̶i̶l̶l̶ ̶b̶e̶ ̶h̶o̶r̶r̶i̶b̶l̶y̶ ̶o̶v̶e̶r̶w̶r̶i̶t̶t̶e̶n̶ ̶b̶y̶ ̶t̶h̶e̶ ̶l̶a̶t̶e̶s̶t̶ ̶c̶o̶o̶k̶i̶e̶.̶ ̶W̶e̶ ̶n̶e̶e̶d̶ ̶t̶o̶ ̶c̶l̶e̶a̶n̶u̶p̶,̶ ̶s̶o̶ ̶l̶e̶t̶'̶s̶ ̶a̶d̶d̶ ̶a̶ ̶'̶b̶u̶r̶n̶ ̶a̶f̶t̶e̶r̶ ̶r̶e̶a̶d̶i̶n̶g̶'̶ ̶l̶i̶n̶e̶ ̶t̶o̶ ̶o̶u̶r̶ ̶p̶h̶p̶.̶ ̶W̶e̶ ̶s̶e̶t̶ ̶t̶h̶e̶ ̶c̶o̶o̶k̶i̶e̶ ̶d̶a̶t̶e̶ ̶b̶a̶c̶k̶ ̶i̶n̶ ̶t̶i̶m̶e̶ ̶b̶e̶f̶o̶r̶e̶ ̶u̶n̶s̶e̶t̶t̶i̶n̶g̶ ̶t̶o̶ ̶b̶e̶ ̶s̶u̶r̶e̶ ̶t̶h̶a̶t̶ ̶t̶h̶e̶ ̶u̶n̶s̶e̶t̶ ̶r̶e̶a̶l̶l̶y̶ ̶h̶a̶p̶p̶e̶n̶s̶:̶

7.  Skip steps 5 and 6: I knew I couldn't set the value of an input element with innerHtml in js, so made one big workaround, when I could simply set the value with 'value='. So I can remove all of the setValue part of my beforesave function in the delegate class, and rewrite the callback function in my javascript as follow, removing the cookie setting line. I'm not removing the steps completely to leave trace of how there are  always many different ways to achieve something. Of course doing this without cookies and with one line of code I feel a lot better.

callback : function(data){
        document
.getElementById('customer').value=''+data.name+'';
   
}

This code goes inside our addCustomer function, like this:
function addCustomer(){

 
var dlg = new xataface.RecordDialog({
 table
: 'customers',
callback
: function(data){
        document
.getElementById('customer').value=''+data.name+'';
   
}

 
});
 
  dlg
.display();
 
}

You can add more arguments, like selecting the fields (you might want to limit the input to the basic fields) or setting the height and width of the pseudowindow, or set the title... all of the options are detailed in the link above. 


8. Let's give our button a little bit of style, pure css.

span.button a {
    text
-decoration: none;
    color
: #000;
    border
: 1px solid #999;
    border
-radius: .4em;
    padding
: .4em;
    background
: #eee;
    box
-shadow: inset 0px -0.4em 0.4em 0px #ccc;
}
span
.button a:hover {
    background
: #ccc;
    box
-shadow: inset 0px -0.4em 0.4em 0px #eee;
}
or something better.


8. Copy/paste the php paragraphs to the delegate class files of the other tables where you need this button. Don't forget to include the block__custom_javascripts() thing.



RecordDialog is a brilliant instrument. As many other parts of xataface, it shows how flexible and well done this framework is. And how lacking in the documentation/tutorials department - no offense to Steve Hannah, he did such a great job and deserves every bit of credit for it - I'm only suggesting that we should contribute when we can, which is what I'm trying to do with this post. I hope somebody finds this useful.

Rangan V

unread,
Feb 22, 2021, 3:30:19 AM2/22/21
to Xataface


I like the approach followed and am partially successful in my attempt.

I have two tables 'sohead' and 'sodtl' both of which have fields 'sono' and 'sonosq'.
Based on the above, when I create a new 'sohead' record the sono number is autogenerated. This number needs to be passed on to the new 'sodtl' record that I am creating. The callback function does not work for me as documented. Where am I going wrong ?
My sohead.php file

My javascripts.js file
QUOTE
//require <RecordDialog/RecordDialog.js>



function addsodtl(){


 var dlg = new xataface.RecordDialog({
 table: 'sodtl',
callback : function(data){
        document.getElementById('sono').value=''+data.sono+'';
        document.getElementById('sonosq').value=''+data.sonosq+'';
   }

 });
 
  dlg.display();
 }
UNQUOTE
sohead.php

Antonio Sottocasa

unread,
Feb 26, 2021, 6:37:10 AM2/26/21
to Xataface

So from my understanding you are trying to create a new 'sodtl' from a button on the new 'sohead' and pass data from sohead to the sodtl, right?
If I understand correctly there are two major problems here:
1. The scope of the example is quite the opposite: you pass data from the record you create in the AJAX window (so in your case the 'sodtl') to the one you were originally writing but haven't saved yet (so in your case the sohead)
2. Autogenerated fields are normally generated upon save, so before you save you don't have a sono number yet.

But I see that you already have a function in your php to generate the sono, you should only wrap it in a variable and then pass it to the javascript like this:

 echo '<span class="button" onclick="addsodtl(\".$yourvariable .\")"><a href="#">Add SO Detail</a></span>';

and then use it in your javascript like this:
function addsodtl(sono){

 var dlg = new xataface.RecordDialog({
 table: 'sodtl',
params : { sono: String(sono),
   },

callback : function(data){

   }

 });
 
  dlg.display();
 } 
Reply all
Reply to author
Forward
0 new messages