« [Tip] Popping UP the popupmenu plugin | Main | [News] Announcing Mosaic Frameworks for Servoy »

September 13, 2007

[Article] Alternatives to Using Global Dataproviders

by Adrian McGilly
McGilly Information Systems, Inc - Servoy Partner

Introduction

When writing form methods, I often end up creating global dataproviders to store informaton that needs to be shared among the form's methods, but which is NOT relevant to any other part of the solution. For example, I might have state information about the form that needs to be managed by the form's various methods. I can always create a global dataprovider to hold this kind of information but I have two problems with this approach. First, a global is overkill because I don't need the variable to be global in scope - I only need this form's methods to see it. Second, I find that my list of global dataproviders can get pretty long and disorganized if I create too many of them, so I prefer to avoid it when possible.

As of v 3.5 of Servoy we are able to have multiple forms open at once, with the possibility that users will move freely among the simultaneously open forms. With this new feature, the need to manage state information for multiple open forms will become more commonplace. It would be nice if this didn't mean creating more global dataproviders.

There are several ways to achieve the sharing of information across all of a form's methods that don't involve global dataproviders. This article describes some of them and their pros and cons. Thank you to Sean Devlin and Robert Ivens for their contributions to this article.

Approach #1: using the 'this' keyword

Javascript provides a keyword called 'this' which refers to the 'current object'. When used in a form method, 'this' refers to the form the method belongs to. Using the 'this' keyword you can easily create "variables" that 'belong' to the form and are visible to all the form's methods just by saying this.myVar = <value>. For example, you could create a "variable" called dataEntryMode that is visible to all methods on the customers form and assign it a value of 'ADD' just by putting the following code in a form method for the customers form:

this.dataEntryMode = 'ADD'

(Technically we haven't actually created a variable. What we have done here is add   a property called dataEntryMode to the form object, but you can just think of it as a variable.) From that point any method belonging to the customers form can access the "variable" by referring to this.dataEntryMode, for example:

if (this.dataEntryMode == 'ADD')
          {
               // test for uniqueness
          }

Furthermore, forms NOT belonging to the customers form can access the variable by spelling out the form object instead of using the 'this' keyword. For example a global method, or a method belonging to any form other than the customers form, could access the "variable" by referring to:

forms.customers.dataEntryMode

or assign it a new value such as:

forms.customers.dataEntryMode = 'EDIT'

or create a new "variable" on the form and assign it a value, for example:

forms.customers.validationState = false

There's something you need to be careful about when doing this. Each Servoy form object comes with a lot of properties already built into it, and you need to be careful when you add a property that you aren't using the name of an existing property, otherwise you will just be overwriting the existing property. In Servoy form objects there is a built-in property for each of the columns of the table that the form is base on. So if you do this:

forms.customers.first_name = 'Sam'

and there is a column called first_name in the table that the customers form is based on, you've just assigned the value 'Sam' to that column. If you always prefix your variable names with something like 'fv_' (for form variable) this problem should be easy to avoid. If you're curious to see the complete list of properties on a Servoy form object, just run this code in a form method:

for (var key in this)
          {
               application.output(key + '=' + this[key])
          }

Approach #2: declaring global variables in javascript (NOT in Dataproviders)

Just as you can declare local variables in your methods using the var keyword, Javascript lets you declare global variables right in your Javascript code. For example the following line of code:

globals.myGlobalVar = 99

creates a global variable called globals.myGlobalVar and assigns the value 99 to it. Note that this is NOT the same as creating a local var, which looks like this:

var myLocalVar = 99

This global var is visible to all methods in your solution, however global variables created this way are not the same as global dataproviders. They don't show up in the list of global dataproviders and they can't be placed directly on forms the way global dataproviders can. But in all other respects they behave the same as global dataproviders and can come in handy for sharing information across a form's methods.

Note that the global var is only 'available' once it's been created, and it's created the first time it appears on the left hand of an assignment such as

globals.myGlobalVar = 99

If you want to declare the global var without assigning a value to it, do this:

globals.myGlobalVar = null

It's  important to make sure that the code that 'declares' these globals runs before any code that tries to access them. If a method tries to access the variable before it's been declared, you'll get a fatal 'variable undefined' error.

Approach #3: create 'formvars' using an associative array

Here's an approach that I call "formvars" which allows you to create variables that are global, but which are "organized" by form.  All you need to do is download include my formvars module in your solution and call its globals.initFormVars() method during the startup of your solution. This will execute these two lines of code:

globals.formvars = new Array()
          for (var i in forms.allnames) globals.formvars[forms.allnames[i]] = new Array()

This creates an associative array that is indexed by the names of all the forms in your solution. Don't worry if you're not sure what that means - you don't need to. Just know that from that point on, you can create and access variables that are global in scope, but which are "organized" by form. What does this mean?

It means you can create global variables that look like this:

globals.formvars.<myform>.<myvar>

Let's look at an example. Let's say you want a variable called "data_entry_mode" that is visible to all methods on a form called "students", and you want to set that variable to contain the string 'ADD'. You can now do this:

globals.formvars.students.data_entry_mode = 'ADD'

Now any method in your solution can access that variable simply by referring to :

globals.formvars.students.data_entry_mode

If you want to create the formvar without initializing it to a particular value, you could do this:

globals.formvars.students.data_entry_mode = null

Later if you are working on another form called teachers and you need a similar formvar for that form, you can create it using the same name without disturbing its counterpart on the students form. In other words you can do this:

globals.formvars.teachers.data_entry_mode = 'ADD'

Furthermore, though these formvars are "organized" by form, they are in fact global in scope. So there is nothing stopping a method belonging to the teachers form from accessing a formvar on the students form. Similarly global methods can access any formvar.

Some advantages of the formvar approach  over the approaches mentioned above include:

  1. All of your solution's formvars are now in fact held in a  a single global variable called globals.formvars which is in fact an  associative array. This makes it easy to save all those variables and their values in your database if the need arises: just stick it in a media field in the db. This can be a handy way of saving all of your user's UI state information for your entire solution.
  2. There's no danger of accidentally overwriting a "built-in" property of a form or a database value, as is the case with the 'this' keyword approach #1 outlined above.
  3. It allows for form-centric trash management. For example if you know that when the user closes or leaves a particular form you no longer need its formvars, you can issue the command

                globals.formvars.<myform> = new Array()

    and that will delete all that of <myForm>'s formvars and free up any memory they were using. See also the deleteFormvars() and clearFormvars() functions described below, which facilitate form-centric clearing of all formvars.

Some   disadvantages of this approach are:

  1. It can make for fairly long variable names, however, my McGilly_formvars module includes some functions that address this - see below...
  2. If a form is renamed and you've used the above syntax for creating/accessing the form's formvars, you will need to do a search and replace on your code to rename the form in the formvar references. Again, my McGilly_formvars module includes some functions that can help you avoid this situation- see below...

Recognizing these limitations of the formvars technique, I added the following global methods to the McGilly_formvars module:

Function globals.setFormvar(myVar, value, [myForm])
Description

Sets the value of the formvar called myvar to <value> for the form myForm. If the myForm parameter is omitted, it assumes the 'calling' form (i.e. the form the calling method is attached to).

Note that you can only omit the myForm parameter if you are calling this functions from a form method. If calling from a global method, the myForm parameter is required or else you will get an error).

Examples

globals.setFormvar('data_entry_mode', 'ADD')

//sets a formvar called data_entry_mode to hold the value 'ADD' for the calling form. This is the same as saying:

globals.formvars.<calling form name>.data_entry_mode = 'ADD'

//Another example, this time using the [myForm] parameter:

globals.setFormvar('data_entry_mode', 'EDIT', 'customers')

//sets a formvar called data_entry_mode for the customers form to hold the value 'EDIT'. This is the same as saying:

globals.formvars.customers.data_entry_mode = 'EDIT'

 

Function globals.clearFormvars([myForm])
Description

Clears (i.e. assigns a null value to) all the formvars for the form myForm. If the myForm parameter is omitted, it assumes the 'calling' form (i.e. the form the calling method is attached to).

Note that you can only omit the myForm parameter if you are calling this functions from a form method. If calling from a global method, the myForm parameter is required or else you will get an error).

Examples

globals.clearFormvars()

//sets to null all the formvars for the calling form.

//Another example, this time using the [myForm] parameter:

globals.clearFormvars('customers')

//sets to null all the formvars for the customers form.

 

Function globals.deleteFormvars([myForm])
Description

deletes all formvars for the form myForm. If the myForm parameter is omitted, it assumes the 'calling' form (i.e. the form the calling method is attached to). Once deleted, a formvar will no longer appear in the list returned by globals.listFormvars() (see below)

Note that you can only omit the myForm parameter if you are calling this functions from a form method. If calling from a global method, the myForm parameter is required or else you will get an error).

Examples

globals.deleteFormvars()

//deletes all formvars for the calling form. This is the same as saying:

globals.formvars.<calling form name> = new Array()

//Another example, this time using the [myForm] parameter:

globals.deleteFormvars('customers')

//deletes all formvars for the customers form. This is the same as saying:

globals.formvars.customers = new Array()

 

Function globals.listFormvars([myForm])
Description

Returns a string listing all the formvars for the form myForm. If the myForm parameter is omitted, it assumes the 'calling' form (i.e. the form the calling method is attached to).

Note that you can only omit the myForm parameter if you are calling this functions from a form method. If calling from a global method, the myForm parameter is required or else you will get an error).

Examples

globals.setFormvar('test1','hello world', 'main')
            globals.setFormvar('test2','99', 'main')
            globals.setFormvar('test1','Sunday', 'lst_orders')
            globals.setFormvar('test2','100', 'lst_orders')
            

application.output(globals.listFormvars('main'))
                application.output(globals.listFormvars('lst_orders'))

//here is the output:

Formvars for form main:
                   test1 = hello world
                   test2 = 99

Formvars for form lst_orders:
                   test1 = Sunday
                   test2 = 100

| Posted by Adrian Mcgilly on September 13, 2007 at 05:35 PM in Articles | Permalink

Comments

Thank you very much Adrian for sharing and the in-depth explanations - good stuff!

Posted by: Ben Savignac | Sep 14, 2007 6:26:22 AM

Note: On SW07 last week Servoy announced to include form instance variables in version 4.0

Posted by: Paul | Oct 15, 2007 1:35:03 PM

Post a comment