Gyroscope / Adding a Record
So far we've been creating landlord and property records in the database.
Next we'll create interfaces for adding them directly in the application.
First we need a trigger to invoke a form for adding a new landlord record.
In
listlandlords.inc.php after the search box add the following link:
<div style="padding:5px 0;">
<a onclick="addtab('newlandlord','New Landlord','newlandlord');">
<img src="imgs/addrec.gif"> add landlord
</a>
</div>
Remember the first parameter in the addtab function is the key of the tab. In
this case, we have a fixed value. This means we can have only one New
Landlord form at any moment, which is suiting.
Next is the switch - no surprise here:
case 'newlandlord':
include 'icl/newlandlord.inc.php';
newlandlord();
break;
To create
newlandlord.inc.php, we can conveniently copy and paste the code
from
showlandlord.inc.php with a few modifications:
<?php
function newlandlord($llid=null){
if (!isset($llid)) $llid=GETVAL('llid');
$llid='new';
global $db;
?>
<div class="section">
<?
$query="select * from landlords, persons
where landlords.personid=persons.personid
and landlords.llid=$llid ";
$rs=sql_query($query,$db);
$myrow=sql_fetch_array($rs);
$fname=$myrow['fname'];
$lname=$myrow['lname'];
?>
<table>
<tr><td>Firstname:</td>
<td><input id="llfname_<?echo $llid;?>" value="<?echo $fname;?>">
</td></tr>
<tr><td>Lastname:</td>
<td><input id="lllname_<?echo $llid;?>" value="<?echo $lname;?>">
</td></tr>
<tr><td></td>
<td>
<button onclick="addlandlord('<?echo $llid;?>');">
Add Landlord
</button>
</td></tr>
</table>
</div>
<?
}//func
And here's the cleaned-up version:
<?php
function newlandlord(){
$llid='new';
global $db;
?>
<div class="section">
<table>
<tr><td>Firstname:</td>
<td><input id="llfname_<?echo $llid;?>">
</td></tr>
<tr><td>Lastname:</td>
<td><input id="lllname_<?echo $llid;?>">
</td></tr>
<tr><td></td>
<td>
<button onclick="addlandlord();">
Add Landlord
</button>
</td></tr>
</table>
</div>
<?
}//func
Remember to leave out the one-to-many sections such as phone numbers or
properties in the new record form. Once the record is created, the user will
see the detail view where associated data can then be added.
On the JavaScript side, we can also reuse updatelandlord:
addlandlord=function(llid){
var llid='new';
var ofname=gid('llfname_'+llid).value;
var olname=gid('lllname_'+llid).value;
fname=encodeHTML(ofname);
lname=encodeHTML(olname);
reloadtab('landlord_'+llid,
ofname+' '+olname,
'updatelandlord&llid='+llid+'&fname='+fname+'&lname='+lname,
function(){
if (document.viewindex==0)
showview(0);
}
);
}
Note that we cannot use ajxpgn, addtab or reloadtab (unless you're using V3.2 or greater) to inject the view of the
new record into a pre-defined container, as we've been doing with updating
records before. This is because the containers require a unique ID, which is
assigned by the database.
The solution is to send a request to add the record. Get the new record's ID
from the response and open a new tab while closing the New Landlord tab.
addlandlord=function(llid){
var llid='new';
var ofname=gid('llfname_'+llid).value;
var olname=gid('lllname_'+llid).value;
fname=encodeHTML(ofname);
lname=encodeHTML(olname);
var res=ajxb(document.appsettings.codepage+
'?cmd=addlandlord&fname='+fname+'&lname='+lname);
var llid=parseInt(res,10);
if (res!=llid) {
alert('oops, something went wrong');
return;
}
closetab('newlandlord');
showlandlord(llid,ofname+' '+olname);
if (document.viewindex==0)
showview(0);
}
The bolded lines in the above code ensure the response is a numeric value.
The server response should not contain any extra white spaces.
Next we add the switch clause:
case 'addlandlord':
include 'icl/addlandlord.inc.php';
addlandlord();
break;
and implement the handler:
<?php
function addlandlord(){
$fname=GETSTR('fname');
$lname=GETSTR('lname');
global $db;
$query="insert into landlords (fname,lname) values ('$fname', '$lname')";
$rs=sql_query($query,$db);
$llid=sql_insert_id($db,$rs);
echo $llid;
die();
}
The
die statement instantly terminates the script, eliminating all potential
white spaces.
Starting Version 3.2, we can write the JavaScript handler for creating a new
landlord record in a simpler and more robust manner:
addlandlord=function(){
var ofname=gid('llfname_new').value;
var olname=gid('lllname_new').value;
fname=encodeHTML(ofname);
lname=encodeHTML(olname);
reloadtab('newlandlord',
'addlandlord&fname='+fname+'&lname='+lname,
function(req){
var llid=req.responsetText;
if (llid!=parseInt(llid,10)){
alert('oops, something went wrong');
return;
}
reloadtab('newlandlord',ofname+' '+olname,
'showlandlord&llid='+llid,null,null,
{newkey:'landlord_'+llid});
if (document.viewindex==0) showview(0);1
}, null
);
}
1 Starting v4.6, use
if (document.viewindex==0) reajxpgn('landlordlist','lv0');
Compare the above code with the previous listing. The first AJAX call
creates a new record on the server side. It was initiated by the
ajxb function,
which blocks the execution until server response. Once the return value is
validated, the original tab is closed and a new tab is added by calling the
addtab function.
The new version uses
reloadtab function for the initial call. We may take
advantage of the data parameter to deal with lengthy POST fields such as a
description.
The
reloadtab function ensures that only one request is sent. Subsequent
requests are ignored until the arrival of the first response. This feature is only
available starting 3.2.
In the callback function we could use the old fashioned "close tab, open new
tab" sequence. Or we could simply "rekey and reload" the tab, though the
process of closing and opening is noticeable clumsy.
Sync Listview Content in Context
When a record is added, updated or deleted, the associated list view should be synchronized. Before v4.6, we used the following line to
detect whether the list view is visible, and if so, reload the list view:
if (document.viewindex==0) showview(0);
The issue with the previous approach is that the view index alone does not identify the state of the list view. It is possible that the user is looking
at the second page of the list, or that the list is bearing a search keyword. Page- and keyword specific content is loaded in a sub container of the list view; in this case, "landlordlist".
Starting v4.6, Gyroscope sports an enhanced version of NanoJS. Each time ajxpgn is called, the target container stores how it is loaded for a future replay.
The new
reajxpgn function replays in the first ajax container first; if the container doesn't have reload parameters set, then it falls back to a secondary loader:
if (document.viewindex==0) reajxpgn('landlordlist','lv0');
Why are the reload parameters not always loaded for the
landlordlist container? When a container is loaded with
ajxpgn, its reload params are guaranteed to be set.
However, when the list view
lv0 is first loaded, the
landlordlist is loaded as part of the injected content. The second parameter in the
reajxpgn function falls back to the main loader,
offering the same behavior as
showview in previous versions of Gyroscope.