[Article] Look before you leap: Are you in?
by Chris Cain
Extensitech
Would you like to check whether a certain field, method or element exists, and then branch your methods accordingly? For example, would you like to perform a method onShow for all forms, but for those with a certain button, also determine whether the button should be visible?
The “in” operator allows you to check whether one element in the Solution Object Model exists “in” another.
In the “tree” shown in the Editor, the “in” operator allows you to check for any object within any other object. For example:
"OKButton" in forms[currentcontroller.getName()]
will return true if there’s an element called “OKButton” on the current form. This basically lets you “look before you leap”; before trying to execute a method, or act on an element, you can check whether it’s “in” the form you’re on, and save yourself the “undefined value has no properties” error.
(Note one important exception: you can’t check for the existence of a form in Forms, since Servoy only loads forms “as you go”. Instead, use forms.allnames to get an array with all the form names.)
So how could “in” be useful? Let me give you a couple of examples of how we use it:
Common event triggers
For the most part, I want all of my forms to do the same thing when events like onDelete or onFind are triggered. For example, when the user tries to delete a record, I want to give an “Are you sure?” message and then delete if the answer is yes. So my standard, global delete method, which is the onDelete on my template form, would look something like this:
var btn = plugins.dialogs.showQuestionDialog( 'Confirm Delete', 'Delete this record?' , 'No' , 'Yes')
if ( btn == 'Yes' )
{
currentcontroller.deleteRecord();
}
However, there are some occasions where I want delete to do something different. I may want it, for instance, to offer “Void” as an option on Invoices. In that case, I could just write a form method, and replace the onDelete for just that form. I’m lazy, though, and sometimes forgetful. I want my global method to check for this automatically. For that reason, my global delete method looks like this:
//if the current form has its own specific method, run that one.
if ( "actRecDelete" in forms [currentcontroller.getName () ] )
{
forms [currentcontroller.getName () ].actRecDelete ();
}
//otherwise, just follow the default step(s) below
else
{
var btn = plugins.dialogs.showQuestionDialog( 'Confirm Delete', 'Delete this record?' , 'No' , 'Yes')
if ( btn == 'Yes' )
{
currentcontroller.deleteRecord();
}
}
Now when I want something different to happen onDelete for a form, just creating the form method, and naming it appropriately, ensures that it will run instead of the standard routine.
Common Elements
I have a tabpanel on most of my primary UI screens, which I call tabView. I have a global routine that loops through tabView and makes certain tabs visible or invisible, depending upon the user and other context.
On the one hand, I could an onLoad method for each form, but for the most part, all my UI screens load the same way, so I want to use a global onLoad method that allows for exceptions, much like the global delete method above.
On the other hand, I can’t add a reference to the “tabView” tabpanel to that global method, because there are a few forms that don’t have tabView.
With “in”, though, I can check for tabView, and only run the visibility method where it’s needed! My global onLoad method, then, looks something like this:
//if the current form has its own specific method, run that one.
if ( "actRecLoad" in forms[currentcontroller.getName()])
{
forms[currentcontroller.getName()].actRecLoad ();
}
//otherwise, just follow the default step(s) below.
else
{
if ( "viewTab" in forms[currentcontroller.getName()].elements)
}
globals.g_subFrmViewEnable();
Now I have a single, global method that I can edit to affect the load of all my forms, and I can add steps that only run under certain conditions, such as when the “viewTab” tabpanel is present.
Location Customizations
“In” can also be used to branch based on the user’s group or location. Consider this scenario: ABC Company has a Contact Data Entry form, where users enter Name, Address, Phone, etc. There’s a button on the form for printing out the information.
Now let’s suppose that the ABC Company has 12 offices around the country. The print button works fine for most of the offices, but there are a couple of exceptions. In office 4, the office management is adamant about reducing paper, and insists that the print button must print to a pdf. In office 8, state law requires the operator to read the caller a statement about privacy, and the company wants a reminder dialog to show before a user can print the data out, saying “Did you read them the privacy statement?” As these requirements crop up, you begin to expect that you haven’t heard the end of these exceptions!
With “in”, you can create methods for each location exception, and use the location number to determine which method to use when the user clicks the print button.
Let’s assume that the location is stored in a global variable we’ll call g_location. The standard print method is called “prtDataEntry” and the location exceptions (one for office 4, to print to pdf, and one for office 8, with the privacy script reminder) are named the same, but with location numbers: “prtDataEntry4” and “prtDataEntry8”, respectively.
(We’ll also introduce another useful function, “Eval()”. Eval looks at the string (in this case a method name), and evaluates it as though it were a line in the method).
The method for prtDataEntry could look like this, then:
var custMethodName = forms.dataEntry.prtDataEntry + g_location
if (custMethodName in forms.contact)
{
eval (custMethodName) // to run, for example, forms.dataEntry.prtDataEntry4
}
else
{
form.contactPrintout.showPrintPreview()
}
Had we just coded the method to run a method with the location number at the end, the method would have produced an error whenever a user was from a location that didn’t have an exception method. This way, though, not only will the method run correctly for all locations, but when office 2 decides that it needs to change the button, too, you’ll just need to add a method called “prtDataEntry2” and you’re done!
Conclusion
With all of these examples, there are alternative means of accomplishing the same thing. However, for cases where there are common functions for most cases, but a number of important exceptions, the “in” operator allows you to have Servoy check for itself whether there is an exception, rather than having to add the exception method and then also change your referring methods to check for it.
All in all, I find that “in” is a valuable addition to our toolbox. It helps us to keep the UI standardized, but not too rigid; we use global methods for “most cases”, but we can quickly add form-level exceptions as the need arises. For multi-location clients, it allows us to customize one location without either locking the other locations into the change or having to create “if” clauses for each individual exception.
I hope you’ll find “in” a valuable addition to your toolbox, too!