Applicability FootPrints Service Core version 11 or later, but not including version 12. Pre-requisites
Keeping the JavaScript in an external file also facilitates easier maintenance of the code. You can write your code in your favorite text editor instead of having to use the Form Designer interface. You can also keep as many different versions and backups of the code as you need just by copying the file to different names.
- Access to Form Designer in one or more workspaces
- Access to the file system on FootPrints application server
- Ability to write JavaScript
- (Suggested) Familiarity with the Sencha Ext JS framework (version 3.4, specifically)
- (Suggested) Familiarity with the HTML Document Object Model (DOM)
Adding a Custom HTML element to the incident form
The “Custom HTML” form element is listed under the “Special Features” section of the Form Designer palette. Figure 2 – Form Designer palette with Custom HTML highlighted Because of how the code was written [I’ll come back to this], we are free to place the Custom HTML element anywhere on the form. I prefer to drop it into the area above the tabbed sections so that it will always be prominent when viewing the Form Designer. You should be aware that while the Custom HTML element we are going to add is not going to contain any visible content, it will still occupy a space on the form’s layout grid. If you put it on a row by itself, users will see a blank row on the form. For that reason, I recommend you put it in an unoccupied space on a partially filled row. Figure 3 – Placement of Custom HTML element above tabbed sections Once the element has been placed on the form, click the pencil icon to edit its properties. Figure 4 – Populating the Custom HTML element’s body In this example, set the element’s name to “Incident Management Customizations”. Next, disable Rich Text Mode. Important: you must disable Rich Text Mode on a Custom HTML element that holds raw HTML or JavaScript and enter the following content: <script src=“/footprints/incident_management.js”></script> Set the element’s Permissions and Access properties so that it is visible to both customers and agents. You should always do this, even if you have customization that should not run, let’s say, on the customer version of the incident form. Next, add a dropdown field to the form called Allow Categories to Determine Assignees. This field should have only 1 choice, “Yes”, and should be optional to agents/hidden from customers. That’s it for the step-by-step portion of this tutorial. What remains is to create and populate the Category and Sub Category fields, and to create some AutoField rules that set the assignees field based on combinations of these two fields plus the Allow Categories to Determine Assignees (having a value of “Yes” in each rule). I assume the reader knows how to perform these basic administrative tasks already. I will now move on to highlighting several techniques used in this example that I consider to be best practices.Best Practices
B.P. #1: Delay your code from running until the form has finished initializing
If you have ever experimented with adding JavaScript using the Form Designer before, you have probably had to ask yourself the question “Where is the best place [on the form] to add my code?” Placed too near the top and it might try to address a field before it has been rendered. Adding a fixed amount of delay before the code runs might work most of the time but inexplicably fail at other times. The reason it is a hard question to answer is that browsers use multiple, parallel threads to render the page, fetch resources over the network, and executive JavaScript, and these threads do not execute in any predictable order. The technique I advocate does not make a loose prediction and leaves things to chance. It delays the code from running for as long as needed until it receives a signal that indicates with 100% certainty that the form has finished loading. The code below demonstrates how to call the myCustomCode1 and myCustomCode2 functions after the form has finished initializing. // Your customizations go inside functions whose names are // passed into afterFormInitialized. function myCustomCode1() { alert(“The form is ready.”); } function myCustomCode2() { alert(“I also think the form is ready.”); } Ext.onReady(function() { afterFormInitialized([myCustomCode1, myCustomCode2]); }); function afterFormInitialized(callbacks) { Ext.TaskMgr.start({ run: function() { var el = Ext.get(‘wait’); if (el === null) { //The “Loading…” mask hasn’t been created yet } else { if (!el.isVisible(true)) { Ext.each(callbacks, function(i) { i.call()}); return false; } } }, interval: 50 }); } You can see this technique in practice in our example code: the two functions, setGlobals, and assigneeCustomizations, are passed into afterFormInitialized, delaying their execution until the appropriate time.B.P. #2: Place your JavaScript in a Custom HTML form element
Back in the days before FootPrints Service Core had its Form Designer feature, custom JavaScript had to be injected into the ticket form using what were called “field headers”. Field headers were always tied to a specific field on the form and they were always printed (even if they contained only invisible JavaScript) above the field they were tied to. The equivalent of a field header in version 11 is the “Help Text and Instructions” property of a field. There are a couple of reasons I do not recommend using a field’s “Help Text and Instructions” property to hold your JavaScript. The first is that it makes the JavaScript hard to locate: you cannot see at a glance whether a field’s Help Text and Instructions holds JavaScript, actual instructions for the user, or both. You might be thinking that keeping track of where JavaScript is located can be made into a straightforward task by always putting the JavaScript in the instructions property of the particular field it affects, but what you have to remember is that not all customizations target a specific field. The other reason I do not use any field’s instructions property is that it forces you to use extra care when changing a field’s visibility to particular user roles or when deleting a field, as the injection of the JavaScript will be affected by such changes. With the introduction of the Form Designer in version 11, you can now add Custom HTML elements to the form. By using one of these to hold your JavaScript, you will avoid the two problems mentioned earlier: you can give the HTML element a name that makes its purpose (holding JavaScript) self-evident, allowing you to quickly locate where your JavaScript resides without drilling down into multiple fields; custom HTML elements are not tied to any fields, allowing you to manage fields and custom JavaScript independently.B.P. #3: Load an external script file
When we created the Custom HTML element, we did not fill it with any actual JavaScript; instead, it only contains a <script> tag that loads an external file containing the JavaScript. The main reason I advocate this practice is that it avoids an issue where + characters can disappear from JavaScript defined directly in the Form Designer. This can be a serious problem given the role + plays in JavaScript syntax:Imagine your code includes the for loop:
One day, you make a change to some unrelated part of the form and publish. Suddenly, no one can create any incidents–the browser just hangs. It turns out that the + characters in the JavaScript disappeared, leaving your code looks like this:
Your incident form now has an infinite loop because the loop’s counter variable, I, never changes! |
B.P. #4: Keep all of your JavaScript for a given workspace together
You might be wondering why I chose to name the Custom HTML element “Incident Management Customizations”, as that name doesn’t give any indication as to what the code does. The file name referenced by this Custom HTML is given a similarly generic name, “incident_management.js”. This is intentional. I recommend defining all customizations for a given workspace in a single file for the following reasons:- Fewer files that have to be transferred to the browser
- More control over the order in which the customizations run
- Less chance of naming collisions resulting from a global function or variable having been defined more than once across multiple files
B.P. #5: Take a JavaScript-only approach to run your code under certain conditions
So far, I’ve advocated using a single Custom HTML element to inject all of your JavaScript onto the form and allowing the entirety of this JavaScript to appear on the form unconditionally, without any regard for whether certain fields referenced by the JavaScript are present for the given user on the given incident at the given time. You might then be wondering how we are going to have the code run only under certain targeted scenarios such as whether the form being displayed is for creating or editing an incident, whether that form is being accessed by a customer- or agent-level user, whether that user has rights to see a certain field, or even whether the time is currently after midnight on the 4th Friday of the month. The answer is to have the code itself check that all the proper conditions for running a certain portion of custom code are met. You can see this technique in our example code where the function setGlobals defines several flags such as isCustomerMode and isDetailsMode that are later checked throughout the code. For instance, our example customization only runs on the create and edit forms opened by an agent: . . . // Only run this customization under certain conditions if (isDetailsMode || isCustomerMode) { return; } . . . If another customization were later added to the JavaScript file, that customization could also make use of these flag variables. The setGlobals function is suitable for reuse in your customizations. The Form Designer lets you show or hide the Custom HTML element that loads your JavaScript based on the user role. If the element is to be shown, then all of the JavaScript will be loaded; it’s an all-or-nothing deal. This JavaScript-only technique, by contrast, produces JavaScript that assumes it will always be present on the incident form and takes responsibility for controlling which parts execute. This allows for much greater flexibility than can be achieved through the Form Designer options, as you can have single lines of code executed or not based on the current user type, or any other condition you care to check.B.P. #6: Check form elements (fields) before attempting to access them
Given that JavaScript is going to be present on all forms, as just mentioned, it follows that your code should always check for the existence of form elements before attempting to reference them. In our example, no assumption was made that the assignee’s field is displayed. Perhaps the user accessing the form is an agent whose role does not allow specifying assignees. Therefore, the entire block of code that tries to reference the assignee’s picker is surrounded by the following conditional statement: if (window.assigneePicker !== undefined) { [do stuff to assigneesPicker] Similarly, the code does not assume that any of the categorization fields are present: var c = document.getElementById(categoryFields[i].fix()); if (c !== null) { [do stuff to c] Whenever I’m not sure whether my code should be compared against null or undefined, I open up my browser’s developer tools and test some on-the-fly JavaScript: Figure 6 – undefined versus nullB.P. #7: Reference fields by their internal names (without needing to know how they’re encoded)
In FootPrints, a field’s internal name is formed by encoding all non-alphanumeric characters in the visible name into a 3-character sequence. Sub Category has the internal name of Sub__bCategory, Phone # is Phone__3, and so on. If you need to access the form input for a field (to get or set its value), you will need to know this internal name because that’s how the form inputs are named. However, you don’t have to know the actual internal name and all of its special character encodings. Your code can make use of the fix and unfix functions that are available on all String objects. This is what we’re doing in our example: var fName = “Let Categories Determine Assignees”; var f = document.getElementById(fName.fix()); It reads a lot better than var fName = “Let__bCategories__bDetermine__bAssignees”;B.P. #8: Don’t change field names
Changing a field’s name through the Form Designer will change the name of the corresponding column in the database and the name of the HTML input on the incident form. This could adversely affect reporting tools that read the database, clients of FootPrints’ web services, and JavaScript customizations that interact with the form inputs. If you do need to change a field’s name, consider using the “Language of Workspace and Address Book Fields” feature to do so, rather than changing the name through the Form Designer. This feature merely changes the displayed name of the field in the graphical interface. Figure 7 – Changing a field’s label but not its internal name Keep in mind that the field’s internal name and therefore the name of the form input for the field will always be the encoded version of its original name; i.e., the name of the field before it has been re-labeled for a particular language.Until next time
If you’ve taken away anything from this tutorial, I hope it’s what I put forward as a re-usable basis for any incident form customization you might ever need to write:- Create a single JavaScript file for the workspace
- Add the afterFormInitialized function to the file
- Add the setGlobals function to the file
- Call afterFormInitialized as follows
- Add a Custom HTML element to your form to load the JavaScript file