You are on page 1of 29

Servoy Tips and Tricks

f r o m The S upp o r t Group

Abou t Thi s Docume n t

We have collected a variety of Servoy tips, tricks and techniques that we learned
from building Servoy applications for our clients. We want to share these with
other Servoy developers to assist you in creating better and more robust
applications. We welcome any comments and feedback you may have at
answers@supportgroup.com.

Abou t The S up po r t G ro up

We are a full-service database design, development and training firm, providing


expert, quality services from project definition all the way through to the final
deployment. We are one of just a few companies who were chosen by Servoy to
be a partner-level member of the Servoy Alliance Network consultants program
and are one of the largest Servoy consultants in the world.

In addition to our custom development services, we offer Servoy Essentials, a 4-


day training class held at our training facilities in Boston, Los Angeles, New York
and the San Francisco Bay Area.

Copyright 2007, The Support Group, Inc.


www.servoysupportgroup.com
O ur L ocati o n s

Boston Area Headquarters Los Angeles


24 Prime Park Way 5200 Lankershim Boulevard
Suite 101 Suite 320
Natick, MA 01760 North Hollywood, CA 91601
(508) 653-8400 (818) 754-1300
(508) 653-2830 (fax) (818) 754-0047 (fax)

Jeff Turner - General Manager Nena Guthrie - Managing Director


(508) 653-8400 x211 (818) 754-1300 x203
jturner@supportgroup.com nguthrie@supportgroup.com

New York San Francisco Bay Area


450 7th Avenue 400 South El Camino Real
Suite 3005 Suite 425
New York, NY 10123 San Mateo, CA 94402
(212) 216-9400 (650) 685-4300
(212) 216-9455 (fax) (650) 685-4303 (fax)

Karyn Witzel - Managing Director Nena Guthrie - Managing Director


(212) 216-9400 (650) 685-4300 x215
kwitzel@supportgroup.com nguthrie@supportgroup.com

To learn more about our Servoy design, development and training services, visit
our website at www.servoysupportgroup.com.

Copyright 2007, The Support Group, Inc.


www.servoysupportgroup.com
Tabl e o f C on te n t s

Using a global method to isolate a record

Triggering a method from a hyperlink in an HTML area

How to avoid scrollbars that extend through the header and footer on list/table
views

Writing flexible methods using element and form names

Calling a global method from a calculation

List view or table view?

Seeing all changes to the current record

Global versus form methods

Limiting data broadcasting

Using value lists to avoid record loading

Changing the display of one row in a list view without affecting all rows

Anchoring basics

controller.loadRecords(dataset) versus
controller.loadRecords(query)

Modularizing

Real-time logging

Printing in the background

Using the foundset updater

Dealing with multiple keys

Looping through records: two ways

Leaving breadcrumbs

Copyright 2007, The Support Group, Inc.


www.servoysupportgroup.com
Usi ng a gl obal met h od t o i sol ate a recor d

There are many times that a single record needs to be loaded into a form. This
may be necessary for exporting, printing, duplication or security reasons. Here’s
a global method that allows one record to be loaded, given the primary key of the
record to load and the form name. This technique uses a SQL query in
conjunction with controller.loadRecords(), which is more reliable and
faster than controller.find():

var theForm = arguments[0];


var theRecordID = arguments[1];
var theTable = forms[theForm].controller.getTableName();
var theTableObject =
databaseManager.getTable(forms[theForm].controller.getServerName(),
theTable);
var thePrimaryKeyColumn = theTableObject.getRowIdentifierColumnNames();
thePrimaryKeyColumn = theTable + "." + thePrimaryKeyColumn[0];

var theQuery = "SELECT " + thePrimaryKeyColumn + " FROM " + theTable +


" WHERE " + thePrimaryKeyColumn + " = '" + theRecordID +
"' ORDER BY " + thePrimaryKeyColumn + " ASC";

forms[theForm].controller.loadRecords(theQuery);

This method would be called like this:

globals.loadSingleRecord("frm_company", 8);

Copyright 2007, The Support Group, Inc.


www.servoysupportgroup.com
Tri g geri n g a me th o d f r o m a hy perli nk i n an HTML area

One of Servoy’s great interface features is the ability to display HTML formatting
in fields set to the display type “HTML area.” The HTML area allows the
developer to build a data-driven interface that is created at run time, rather than
dragging and dropping objects onto a layout. The HTML area can even have
clickable buttons and hyperlinks to call methods. For example, here’s the some
HTML that triggers the load_selected_contact method and passes in a
contact id:

<a href='javascript:load_selected_contact(447099)' class='name'><b>Test


Customer</b></a>

It's possible using this technique to create an entirely dynamic form, based on
database values, and presented using an HTML area that users can interact
with. Here’s a calendar, where the headings on each appointment are hyperlinks
that pass the appointment ID to a method that shows the appointment details:

Copyright 2007, The Support Group, Inc.


www.servoysupportgroup.com
How to av oi d sc roll bar s t ha t exten d t h ro u gh t he
header an d fo o te r on li s t/ table views

To some fastidious developers, the fact that Servoy’s scrollbar extends through
the header and footer on list and table views is visually unappealing and slightly
misleading in terms of functionality. To get around this issue, you will need to
create an extra form that contains the list or table view without headers and
footers. Then, on your main list view, set up the header and footer, set the
display type to record view and expand the body section to the size of the
other “detail” view screens in your application. Then place a tabpanel in the body
section that looks over to your “real” list view screen and set the anchors to all
four sides. Now you have a list view with a better-looking and functionally-
accurate scrollbar.

Copyright 2007, The Support Group, Inc.


www.servoysupportgroup.com
W ri ti n g flexi ble me t ho d s u si ng elemen t and f o r m
name s

Servoy’s flexibility at runtime is one of its defining features: forms, HTML and
SQL can all be modified while the application is running to produce results that
are reflective of users’ preferences, actions and the data in the database. Even
methods can be left somewhat open-ended by using Servoy’s bracket notation
when referring to forms and elements. The method below can change the
foreground (font) color of any button that triggers it, by determining which form
and element caused the event and then using bracket notation to modify that
element:

var theForm = application.getMethodTriggerFormName();


var theElement = application.getMethodTriggerElementName();

forms[theForm].elements[theElement].fgcolor = '#ff0000';

Copyright 2007, The Support Group, Inc.


www.servoysupportgroup.com
Calli ng a gl obal me t ho d f r o m a calculati o n

WARNING: This is undocumented for a reason: if misused it can slow your


solution to a halt.

Have you ever found yourself running the same minor routines in a group of
calculations and wished that you could factor out the common parts as you can in
the method editor? Although it is undocumented, this is possible in Servoy’s
calculation editor by triggering a global method.

This technique should only be used for very simple and non-processor-intensive
routines, such as converting fractions to decimals or formatting a piece of HTML.
Global methods that are to be called from calculations should not affect the
interface directly, nor should any potentially time-intensive functions be used,
such as SQL queries or calls to remote servers.

Copyright 2007, The Support Group, Inc.


www.servoysupportgroup.com
List view o r table view?

The short answer is: table view, whenever possible. Servoy’s table view is much
more efficient at loading medium to large datasets than list view. Table view
takes data returned from a query and places it in a grid, whereas list view must
“paint” each row. For long lists or situations where bandwidth is limited, a change
from list view to table view and make the application feel twice as fast. Table
view is also able to take advantage of almost all of formatting niceties available in
list view: alternating colored rows, highlighting the selected record and HTML
display.

List view:

Table view:

Copyright 2007, The Support Group, Inc.


www.servoysupportgroup.com
Seei ng all change s to t he cur re n t recor d

Servoy’s one click logging is one of its most powerful and easy to use features.
Once you enable tracking in the security preferences and select which tables to
track, you can see a log of all changes to a given record, which can be handy for
both auditing and reverting data. The one difficulty comes in how Servoy stores
the primary key data of the record being modified, as it must take into account
tables with more than one primary key. Here’s a quick global method that will
allow a user or administrator to quickly pull up a list of all changes to the currently
viewed record. This method needs a table name and a record ID to run, though it
would also be possible to derive these two pieces of information and have zero
arguments:

var theTable = arguments[0];


var theRecordID = arguments[1];

forms.log_list_popup.controller.find();
forms.log_list_popup.table_name = theTable;
forms.log_list_popup.column_name = theField;
forms.log_list_popup.pk_data = '%.' + theRecordID + ';%';
forms.log_list_popup.controller.search(true, false);
forms.log_list_popup.controller.sort('event_time asc')
application.showFormInDialog(forms.log_list_popup, -1, -1, -1, -1,
'Edit history’, false, false, false);

Copyright 2007, The Support Group, Inc.


www.servoysupportgroup.com
Global ver s u s f o r m me t ho d s

Figuring out when to use a global method and when to use a form method is one
of the tricks of programming in Servoy that takes some practice. Typically, we
look for code that has the potential for reuse and could be applicable on many
different forms. Because almost all code meets the first criteria, it can be
tempting to start writing methods as globals, even if the opportunity to reuse the
code has not yet presented itself.

There's a reason for Servoy’s form-based method tree structure: it helps you
organize and keep track of the many methods in your solution. Global methods,
on the other hand, appear in one long list and are entirely dependent on good
naming conventions to make them findable. It is better to make methods at the
form level in order to take advantage of Servoy’s built in organization and only
elevate them to global status when it is clear that they are reusable.

Copyright 2007, The Support Group, Inc.


www.servoysupportgroup.com
Limi ti n g da ta b r oadcas ti ng

One way that a developer can reduce the amount of bandwidth and resources
consumed by Servoy is to minimize the amount of data broadcasting that occurs
between clients. Broadcasting is a great feature of Servoy Server but it is often
triggered unnecessarily, causing extra data change messages to be transmitted.
The easiest was to prevent this from happening is to avoid long lists of records
that are of questionable usefulness to the user.

For example, imagine that you have solution that tracks jobs, and every morning
all users open the application and start by viewing the 200 most recent jobs.
Each person has now cached all of these records and as they are changed
throughout the day, data will be broadcasted to all of the clients.

Now imagine that each user typically needs to look at their own jobs and only
occasionally needs to browse the jobs of others. By loading only the current
user’s jobs into the job list at start up, you can reduce the number of records
cached on each client and thereby make a drastic reduction in the amount of
data broadcasting that will occur.

Copyright 2007, The Support Group, Inc.


www.servoysupportgroup.com
Usi ng val ue li s t s to av oi d rec or d l oadi ng

In a list view, it is common to show information from the table on which the form
is based, as well as information from other, related tables. Showing the related
data means that as each row is loaded, the related records must also be loaded
into Servoy’s cache and these related records are then subject to its data
broadcasting rules. For records that change often and that all users need to view
on a regular basis, it is a good idea to let the cache update as other users modify
records. For more static information, or records that only select users need to
see, it may make sense to avoid loading the related records in the first place.

This can be accomplished by populating a value list when the solution loads.
Imagine that you have an order record, and on that order record you have a
salesperson ID. You wish to show a list of orders and one of the columns that
you wish to show in this list is the salesperson’s name. While you could place a
related field from the salesperson table on your list view, this means that if
someone browses the order list, they will load dozens or perhaps hundreds of
related salesperson records into their cache.

Instead, create a valuelist of salespeople and populate it through a SQL query on


solution startup that shows the salesperson’s name and returns their salesperson
ID. Then place the salesperson ID on the list view layout and apply the valuelist
to that field. Now the query happens once, on solution open, and the related
salesperson records are not loaded as users browse the order list. This
technique can improve performance by limiting unnecessary data transfer and
change broadcasting.

Copyright 2007, The Support Group, Inc.


www.servoysupportgroup.com
Chan gi ng t he di s pl ay of one ro w i n a li s t view wi t h ou t
af fec ti ng all ro w s

There are often situations where it would be useful show different fields or visual
indicators on different rows in a list view. If you have ever tried to use Servoy’s
properties for manipulating elements in a list, such as setting the foreground
(font) color, or hiding or showing a field, you know that this change will affect
every row in the list, not just a single row as you might hope. The solution is to
use a combination of a calculation and HTML to change the display based on the
row and its data. There are three ways to use this technique.

The first is to change the display of only one field: perhaps we want to show an
amount due field in a red font when payment is late. We could do this by creating
a calculation field that produces HTML to color the field:

if(status == "Overdue")
{
return "<html><body><span color='#ff0000'>" + status +
"</span></body></html>";
}
else
{
return "<html><body><span color='#000000'>" + status +
"</span></body></html>";
}

The second approach is needed when several fields in a row must be altered, or
the entire background color must change. Here’s the HTML for changing the
background color based on database value. This field on the form would stretch
across the entire body section in list view, resulting in certain rows being
highlighted:

if(item_type == "Manufactured items")


{
return "<html><body bgcolor='#fbdede'></body></html>";
}
else if(item_type == "Fabricated items")
{
return "<html><body bgcolor='#ffffcc'></body></html>";
}
else
{
return null;
}

Copyright 2007, The Support Group, Inc.


www.servoysupportgroup.com
The third option is to abandon the list or table view altogether and instead create
a large HTML area in record view. The HTML area is the ultimate “blank slate”,
allowing not only for visual changes to individual rows, but sub-summaries,
vertical columns and all sorts of other advanced display techniques.

Copyright 2007, The Support Group, Inc.


www.servoysupportgroup.com
Anchori ng basic s

Servoy’s anchor property allows the developer to create layouts that stretch as
the user resizes the window. Not only does this allow for the wide range of
screen sizes available in most organizations, it also gives the users a strong
sense that your Servoy solution is “real”, rich desktop application.

Of the three types of form views, table view is the easiest to add anchors to. In
most situations, you can simply select all of the fields in the row and turn on
anchors for the right side (“top” and “left” are already on by default). Then just
make sure to turn horizontal scrolling off on the form and you’ll have a flexible,
expandable layout. You can even resize the different columns and those on
either side of it will also resize accordingly.

When using list view or record view, things are somewhat more complicated. On
a given axis, you can have only a single element stretch, while all of the others
need to be fixed to one or two of the sides. If everything is allowed to stretch as
the screen grows, you can quickly end up with an unusable mess of overlapping
elements. The detail view screenshot on the next page demonstrates one way
that his form could be set up to take advantage of anchors, where the most
important fields and elements are allowed to stretch, and all others are fixed and
set to anchor to the nearest side:

Copyright 2007, The Support Group, Inc.


www.servoysupportgroup.com
Copyright 2007, The Support Group, Inc.
www.servoysupportgroup.com
controller.loadRecords(dataset) vers u s
controller.loadRecords(query)

Servoy’s multipurpose controller.loadRecords() function offers several


different ways to load records into a form. One option is to pass in a dataset that
has been created using databaseManager.getDatasetByQuery(). Another
is to pass in a SQL query (and optionally some parameters) that Servoy will then
use to load the records. But when should one be used over the other, and why?

controller.loadRecords(dataset) works best when you want to load a


fixed number of records into the form all at once. This method is preferable when
you have a highly complicated SQL query with many joins or you want to avoid
fully qualifying each field name – a requirement in loadRecords(query). On
the downside, there is an upper limit to how many records you may load using
this approach, related to how your backend database deals with the SQL
command “IN”. When using this method, you’re actually running two queries.
The first:

var theDataset =
databaseManager.getDataSetByQuery(controller.getServerName(), theQuery,
null, 10000);

retrieves a set of primary keys based on a query. The last parameter in


getDataSetByQuery specifies the maximum number of rows to return, in this
case 10000. We then take those IDs and pass them to loadRecords():

controller.loadRecords(theDataset)

which uses “IN” to search for records that match the given list of primary keys. If
10000 rows are returned in the initial query, only 200-1000 are loaded; then we
have a problem. The other downside is that, if our upper limit for “IN” is 1000
records, Servoy’s standard procedure of loading only 200 records at a time (good
in terms of performance) is ignored and all 1000 of the records are loaded.

controller.loadRecords(query) offers many advantages over the


aforementioned method. First, the number of records that are returned does not
matter. If a query:

var theQuery = “SELECT customer.pk_customer_id FROM customer ORDER BY


customer.last_name ASC”

controller.loadRecords(theQuery);

Copyright 2007, The Support Group, Inc.


www.servoysupportgroup.com
returns one million rows, Servoy will do just what it normally does during a find:
load the first 200 rows and load the rest as they are needed. As an added bonus,
we’re also skipping the second trip to the database that our first method required,
making this method somewhat faster. The only downside is that you must adhere
to certain syntax rules in your query: use fully qualified field names, select only
the primary keys, and always include the “ORDER BY” keyword.

Copyright 2007, The Support Group, Inc.


www.servoysupportgroup.com
Modul arizing

Every solution in Servoy has the potential to become a self-contained, reusable


module with the flip of a combobox. To turn a solution into a module, simply go to
File->Solution Settings and set the type pulldown menu to “Module”. Now when
you go to this same dialog box in another solution, you can add your new module
to the “parent” solution.

Before Servoy 3.1 this approach had


many “pros” and one significant “con”.
On the plus side, modularizing will, at
least in theory, give you a block of
related forms and methods that may
serve as starting point for future
solutions. It would also be possible to
write distinct types of functionality and
combine them into a pre-fabricated, yet
customized, solution for your clients.
Even better, modules can themselves
contain modules, so if you have a very
basic and common set of methods,
global variables and even a basic image
library that you want to include with
everything you build, this “core” module
can be used in higher-level modules,
which then go together to form the main
solution. The major “minus” has
traditionally been that working across
modules was a tedious process, because
it required each individual module to be
closed before you could work on another.

Since version 3.1 this limitation has been removed and it is now possible to edit
modules directly from the main solution. With this power comes new
responsibility, because at the very least, the constant opening and closing of
solutions in previous versions forced the developer to be very clear about where
one module ended and another began. The temptation now that all of the editing
can be done from one place is to blend the solutions to such a great degree that
the point of modularizing (reusability) is lost.

Copyright 2007, The Support Group, Inc.


www.servoysupportgroup.com
Real- ti me l og gi ng

In finding out how fix bugs in your applications, 90% of the battle is getting good
information about what exactly is happening as the program runs. There are
some great tools in the editor portion of Servoy Developer to aid in gathering this
information, from the debugger with its ability to step through methods to the
variable viewer that gives insight into the current values of method variables. The
web-based administration console is also useful in hunting down bugs and
performance issues with the “Server Log” and “Database Performance” sections.

Debugging right in the client is also an option, and this method will sometimes
allow you to click though the application and catch bugs that may have been
missed using the approaches mentioned above.

To debug in client, simply


go to your Java
preferences (in Control
Panel on Windows or in
the Utilities folder on a
Mac)

The second way that we


can gain insight into how
client is running is by
enabling the “stacktrace”
property which will give
information on data
broadcasting and other
low-level functionality. To
do this, open the
servoy_client.jnlp file that
is downloaded when you
launch Servoy from a
browser in a text editor
and add the following line:

<property name="STACKTRACE" value="TRUE" />

as show on the following page

Copyright 2007, The Support Group, Inc.


www.servoysupportgroup.com
<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.0+" codebase ="http//localhost:8080 href="/servoy-client/servoy-
client.jnlp">
<information>
<title>Servoy Client</title>
<vendor>Servoy</vendor>
<homepage href="http://www.servoy.com/"/>
<icon kind="shortcut" href="/lib/images/servoy_client_icon.gif" width="32" height="32"/>
<icon kind="splash" href="/lib/splashclient.gif" width="64" height="64"/>
<shortcut online="true>"
<desktop/>
</shortcut>
</information>
<resources>
<j2se version="1.4+" max-heap-size="256m" />
<property name="apple.laf.useScreenMenuBar" value="true" />
<property name="STACKTRACE" value="TRUE" />
<jar href=/lib/j2db.jar main="true" version="1164288053500" download="eager"/>
<jar href="/lib/js.jar" part="script" version="1156438412000" download="eager"/>

Once this change is made, save it and launch Client by double clicking on the
.jnlp file. You can then use a command line tool such “tail” (Mac/Linux) to view
the log as it is updated:

tail -f /Users/yourusername/.servoy-client-log.txt

Copyright 2007, The Support Group, Inc.


www.servoysupportgroup.com
Pri n ti ng i n the backgr o un d

There are several ways that you can print images stored in “blob” columns from a
Servoy interface: create a form to show the image and print it, convert the image
to a .pdf and send that to the printer or write a method to open the file in external
application and let the user print it from there. Here’s another interesting way to
accomplish this task using Servoy’s ability to execute actions at the command
line, using executeProgramInBackground to employ built in functionality of
Mac OS X or Windows XP.

First, we write out a temporary file from the media field. Then we call the
command line utility that will let us print. On the Mac platform we also have to tell
the command line about the page orientation we want to use.

var theDocument = media_field; // holding a .jpg, .tiff, etc.


var theFileType = file_type // the file extension
var theTempFile = plugins.file.createTempFile('print', theFileType);
plugins.file.writeFile(theTempFile, theDocument);

if(utils.stringMiddle(application.getOSName(), 1, 7) == "Windows")
{
application.executeProgramInBackground('RunDll32.exe',
'shimgvw.dll,ImageView_PrintTo', '/pt', theTempFile,
theSelectedPrinter, '', '');
}
else // for the Mac
{
if(print_orientation == "Landscape")
{
application.executeProgramInBackground('lp', '-o
landscape', theTempFile);
}
else
{
application.executeProgramInBackground('lp', theTempFile);
}
}

Copyright 2007, The Support Group, Inc.


www.servoysupportgroup.com
Usi ng t he f o un d se t up date r

Have you ever needed to update a column for all records in your foundset with a
given value? Perhaps you need to set a flag on all records signifying that they
have been “approved” or perhaps you need to timestamp them all with the
current date and time. Servoy’s foundset updater can come in handy in these
situations, allowing you to update the records in a flash and save you the trouble
of writing a for loop. Here’s an example where we quickly update the
ship_date column on all orders in the current foundset with the current time
and date:

var theShipTime = application.getServerTimeStamp();


var theUpdater = databaseManager.getFoundSetUpdater(foundset)
theUpdater.setColumn('ship_date’, theShipTime)
theUpdater.performUpdate();

The big advantage of this method, as compared with writing your own for loop,
is that this command is issued as a single SQL “update” statement instead of an
update for each row in your current foundset. This is what makes it run
comparatively quickly.

Copyright 2007, The Support Group, Inc.


www.servoysupportgroup.com
Dealing wi th mul ti ple key s

When you create your own database tables in your SQL backend of choice using
the Servoy interface, it’s likely that you’ll make an auto-incrementing numeric
primary key for each table you create. Although this is by far the most common
way of uniquely identifying each record in the table, it isn’t the only way. SQL can
use a combination of values from different fields to create the key for the row.
You may run across this when using Servoy to connect to a pre-existing data
source or to the backend of some piece of proprietary software that uses a
database for storage.

To work with this type of scenario in Servoy, make sure that all of the columns
that make up the key are marked as such in the Dataproviders dialog (sequence
set to “db identity”). Once this is done, all of the normal databinding that happens
by default will work (updates, searches, etc.). The catch comes if you plan to use
controller.loadRecords() in your methods. As described above, there are
a few ways use this function, but both work by passing in primary keys to be
loaded into the form. To load a multi-keyed row, we need to select both columns
and – importantly – we need to pass them to the loadRecords function in
alphabetical order. For a table with two keys some_key and another_key, the
method would look like this:

var theQuery = “SELECT contacts.another_key, contacts.some_key FROM


contacts WHERE contacts.contact_name = ? ORDER BY contacts.contact_name
ASC”

controller.loadRecords(theQuery, [“Mike”]);

Copyright 2007, The Support Group, Inc.


www.servoysupportgroup.com
Loopi ng t h r o ug h recor d s: tw o way s

One of the things that makes Servoy such a flexible tool in working with SQL
databases is that you, as the developer, always have the choice to work through
the interface or outside of it. You can take advantage of Servoy’s built-in
databinding by modifying records and searching using a particular form, or, if
you’d rather, write your own SQL and query the tables directly. The question of
which approach to use comes up often when you need to iterate over a group of
records. If you wish to work within the interface to change a certain value, you
could simply change the selected index and make your change to each row:

var theRecordCount = databaseManager.getFoundSetCount(foundset);

for(var i = 1; i <= theRecordCount; i++)


{
controller.setSelectedIndex(i);
some_field = 56;
}

Note that we use databaseManager.getFoundSetCount() to find out how


many records to loop through. If we instead used
controller.getMaxRecordIndex(), we’d instead get the count of how
many records are currently loaded into the form, which is likely less than the
foundset count. Actually selecting each individual the record is useful if you have
some important method that runs onRecordSelect or if you want to cause
calculations to trigger.

Another approach is to take these changes outside of the interface by getting


each record in a variable, like so:

var theRecordCount = foundset.getSize();

var theFoundset = foundset;


var theCurrentRecord = null;

for(var i = 1; i <= theRecordCount; i++)


{
theCurrentRecord = theFoundset.getRecord(i);
theCurrentRecord.some_field = 56;
}

This approach can be quicker and is safer if your looping method may actually
add records to the foundset. It is generally used for changes to smaller numbers
of records and is especially useful for modifying a related foundset, where you

Copyright 2007, The Support Group, Inc.


www.servoysupportgroup.com
could use the_relation_name.getSize() to figure out how many iterations
need to be done.

Copyright 2007, The Support Group, Inc.


www.servoysupportgroup.com
Leaving breadc ru m b s

Servoy’s built-in history buttons and corresponding history object make moving
backwards and forwards between previously visited forms a breeze. Using the
method editor, we can even access the names of previously visited forms. While
these forms may not have names that are comprehensible to the user, you could
easily create a method to show a list of visited form names to the user.

Here’s a method that gets all of visited form names, matches them to a
corresponding display name (stored in a database table), and puts them in an
array which could be used to create an HTML menu:

var theSize = history.size();


var theVisitedForms = [];

for(var i = 1; i < theSize; i++)


{
var theFormName = history.getFormName(i);
forms.form_name_list.controller.find();
forms.form_name_list.form_internal_name = theFormName;
forms.form_name_list.controller.search();
var theFormDisplayName = forms.form_name_list.form_display_name;
theVisitedForms.push(theFormDisplayName);
}

Even though this type of history is highly useful, databases with hierarchical data
structures can sometimes benefit from a different type of “history” that takes into
account not only where the user has been, but where it fits into the database
structure as a whole. This technique, known as “breadcrumbs” to web designers,
helps users to have a better concept of where they are, gives them easy access
to other points higher in the hierarchy and helps them understand the logic of the
application. Here is a global method that accepts pairs of display names/actual
form names and creates HTML breadcrumbs that will allow users to navigate to
other forms.

var theFormArray = arguments[0];


var theSize = theFormArray.length;

var theHTML = '<html><head>' +


'<style>a {color: #113196;} .button {color: #113196; border-style:
solid; vertical-align:bottom; font: normal 11pt Arial; text-
align:right; height:30px;}</style>' +
'</head><body><table width="100%"><tr><td class="button" height="40"
valign="bottom">';

Copyright 2007, The Support Group, Inc.


www.servoysupportgroup.com
for(var i = 0; i < theSize; i++)
{
theHTML += ' :: <a href="javascript:globals.go_to_form(\'' +
theFormArray[i][1] + '\')">' + // the real form name
theFormArray[i][0] + '</a>'// the display name
}

theHTML += '</td></tr></table></body></html>';
globals.breadcrumb_display_field = theHTML;

It would be called like this (probably onShow of a form):

var theForms = [["Companies", "company_detail"], ["Departments",


"dept_detail"], ["Employees", "employee_detail_main"]]
globals.set_breadcrumbs(theForms);

Which would produce something like this:

:: Companies :: Departments :: Employees

On clicking one of the links, this global method would be called and take the user
to the requested form:

var theForm = arguments[0];


forms[theForm].controller.show();

Copyright 2007, The Support Group, Inc.


www.servoysupportgroup.com