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.