Code Sharing Example

Today I’d like to talk about code sharing within form definitions. We’ll start by talking about the advantages of code sharing and then get specific about techniques to support code sharing in form definitions. We’ll start with the advantages.  For those of you with a programming background, this is review.  But bear with me:

  • Less code.  Once you have a library of shared code, you will write less code in your event scripts.
  • Faster development. Writing less code means faster code development.
  • Lower maintenance costs.  Less code means les code maintenance
  • Consistent behaviours within a form and between forms.  Shared code means that the same action from different places in the same form or between different forms will behave consistently. And it means that changing the behaviour is done by modifying the centralized logic.
  • Simplify complex operations for use by novice form designers.  A shared code library can offer simple interfaces to complicated functions.

But … to get the benefits of code sharing you need to make an investment:

  • Recognize repeated patterns
  • Generalize the repeated operation
  • Isolate the variable parts of the operation into parameters

The generalized, shared version of functionality has higher up-front development costs.  However once the initial investment has been made in shared code, the ROI will more than compensate.

Ok, enough of talking about things you already knew.  Lets walk through a specific example.  I reviewed a form recently that had lots and lots of code to copy field values between subforms. The sort of thing you might do if, for example, you were copying a billing address to a shipping address.

Assuming subforms named S1 and S2 and fields named F1 – F5, the code initially looked like this:

S2.F1.rawValue = S1.F1.rawValue;
S2.F2.rawValue = S1.F2.rawValue;
S2.F3.rawValue = S1.F3.rawValue;
S2.F4.rawValue = S1.F4.rawValue;
S2.F5.rawValue = S1.F5.rawValue;

As written, this is not very good code.  It’s verbose and tedious.  And if any fields are added, removed or renamed, the code will need to be changed.

Now, let’s look at a progression of changes we can make to improve this code.  First of all, a little JavaScript tip. When you see an expression such as S2.F1, it can also be written as S2["F1"].  With that knowledge, we can re-write the script as:

var fields = ["F1", "F2", "F3", "F4", "F5"];

for (var i = 0; i < fields.length; i++) {
   S2[ fields[i] ].rawValue = S1[ fields[i] ].rawValue;
}

It’s a little less verbose, but still fragile.  Let’s change it so that our list of fields is not hard-coded:

var srcFields = S1.resolveNodes("$.#field[*]");
for (var i = 0; i < srcFields.length; i++) {
  var fieldName = srcFields.item(i).name;
  // if the same-named field exists in S2…
  if (S2.nodes.namedItem(fieldName)) {
     S2[fieldName].rawValue = srcFields.item(i).rawValue;
  }
}

Notice that the script starts by using resolveNodes() to get a list of fields from the source subform. It then checks if the same named field exists in the destination subform.  If it’s in both places, we copy a value over. This is a big improvement.  It means that if any fields are added, removed or renamed the script will continue to work.  But we’re still not sharing code.

The next step is to generalize the function:

function subformCopy(dst, src) {
  var srcFields = src.resolveNodes("$.#field[*]");
  for (var i = 0; i < srcFields.length; i++) {
    var fieldName = srcFields.item(i).name;
    // if the same-named field exists in S2…
    if (dst.nodes.namedItem(fieldName)) {
      dst[fieldName].rawValue = srcFields.item(i).rawValue;
    }
  }
}
subformCopy(S2, S1);

Here we’ve isolated the copy functionality into a function. The next step is to move that function into a script object.  Now our script is a one-liner:

utilities.subformCopy(S2, S1);

Great! we copy subform contents in one line of script.  Now to really increase the value, take the script object and make it a fragment so it can be shared between forms:

Once the function has been shared, it can be enhanced or fixed and all forms using it will benefit.  In this case we might choose to make the subformCopy() method handle fields in nested subforms.

Up to this point I’ve talked about using script objects for code sharing.  There are a couple more things to say:

  1. I see people using execEvent() to share code.  e.g. they put script in a change event and use execEvent to call it from the initialize event. I don’t favour this pattern: The code readability is poor.  The performance is not as good (much less overhead to call a script object than it is to call execEvent). 
  2. Propagating events offer another code sharing technique. Isolating functionality in a propagating event means that you write the code in only one script and it is reused in many fields.

And one last point — do you hard-code color values or border widths in your code?  Consider moving these hard-coded values into script objects or form variables. The impact isn’t as dramatic as with shared code, but it is good practise.