Write JavaScript for a UI Model Data Form

Let's focus on how to write custom JavaScript specifically for a UI model form, since this is the most likely scenario where you will need to write JavaScript.

If you have not already done so, go back and read Writing JavaScript for the Infinity Platform and Setting up Visual Studio for JavaScript Development and follow the steps to set up Visual Studio for JavaScript development. This is crucial to writing good JavaScript code for your UI model form. This article also assumes that you know how to create a UI model from a spec, and that you have gone through that process and now have a UI model class and an HTML "snippet" document to go with your data form.

Do You Really Need JavaScript for HTML-based UI Model Data Forms?

When you author an HTML-based UI Model based data form, it's best to avoid writing JavaScript if you can. The process of creating support for HTML-based UI model data forms in the platform is much different than the process of creating support for Winform-based data forms. With UI model, we have the hindsight of thousands of pre-built forms with a pretty good idea of what to expect a developer to need in most cases, so we've built a lot more features into the platform with the hope that writing custom client-side code (in this case JavaScript) would be a rare occurrence. However, we also recognize that there are going to be features that the platform never provides a perfect answer for. For this reason, we provide an "escape hatch" where you can write your own JavaScript to implement these types of forms. Before you open that escape hatch, though, be sure you ask yourself these questions:

Create the JavaScript Document and Reference It from Your HTML Form

After you determine that you absolutely must write custom JavaScript for your form, your first step (after creating your UI model and HTML file, of course) is to create a JavaScript file and add it to your project. As a convention, you should name your JavaScript file with the same name as your HTML file and place it in the same directory as your HTML file. After you create your JavaScript file, you'll need to reference it from your HTML file. This is accomplished by adding a special type of HTML comment to the bottom of your HTML file which contains the prefix "SCRIPT:" followed by the path to the JavaScript file starting after the application's virtual path. There are two reasons why we use an HTML comment to point to the JavaScript file rather than simply using a SCRIPT tag in the HTML itself. The first reason is that different browsers have different rules about how to treat SCRIPT tags inside HTML that is added to a DOM element at runtime. Some browsers will execute script contained in the SCRIPT tags, while others ignore it. By using a library like jQuery to fetch the script separately and let it do its cross-browser magic to get that script to execute after it is fetched, we can get around this issue. The second reason is that the script needs to be executed after the form is rendered and all the fields are mapped. Allowing the platform to load the script independent of the HTML allows it to execute the script at the appropriate time.

Example:

<!--SCRIPT:/browser/htmlforms/constituent/individual/IndividualSearch.js-->

During deployment the JavaScript files will be placed on the same web server that runs the Infinity-based web application such as Blackbaud CRM. The JS file and HTML file are deployed to the same location. The path to the JS file within the HTML should point to the location where your JavaScript file will be copied by your UI model project's post-build step. You may need to check your Visual Studio project's postbuild.bat file to make sure that JavaScript files are copied over and to verify the output path. Typically the xcopy command used in postbuild.bat copies the folder's entire contents including sub-folders, so copying JavaScript (along with any other types of files in your htmlforms folder) should happen automatically.

Prepare Your JavaScript File

Now that you added the reference to your JavaScript file, it's time to set up a couple of standard comments in your code for validation.

Add the JSLint comments from Setting up Visual Studio for JavaScript Development to tell JSLint what rules to follow when it validates your code. It is very important to validate your JavaScript code with a tool like JSLint since there is no "build" step with JS development that could catch syntax or other "compiler" errors. Here are the ones I use:

//use the latest version of jsLint with stricter rule checks
/*lintversion 2*/
/*jslint bitwise: true, browser: true, eqeqeq: true, undef: true, white: true, indent: 4*/
/*global BBUI, Ext, $ */
/*JSLint documentation: http://www.jslint.com/lint.html */

Write the JavaScript for a Data Form

At this point you should be ready to write your JavaScript code. First, create a self-invoking anonymous function. All the code you write for your UI model will be inside of this function.

The first thing you should do is add the "container" and "modelInstanceID" parameters to the outermost self-invoking function.

// Main application entry point
(function (container, modelInstanceId) {
	var util = BBUI.forms.Utility;

})();

The form container object is of type BBUI.forms.FormContainer and hold the instance of your UI model form. The container object allows you to interact with fields on the form with methods such as focusField, updateField, and getFieldByName.

BBUI.forms.FormContainer.getFieldByName

Below is a code sample example of using BBUI.forms.FormContainer.getFieldByName to pull the value of the SOURCEEVENTID form field value into a variable named sourceEventId:

// Main application entry point
(function (container, modelInstanceId) {
var util = BBUI.forms.Utility,
sourceEventId;
 
sourceEventId = container.getFieldByName("SOURCEEVENTID", modelInstanceId).value;
 
})();

BBUI.forms.FormContainer.invokeAction

Below is a code sample example of using BBUI.forms.FormContainer.invokeAction to trigger the firing of a UI Action defined on a data form, just as if a user clicked the UI Action button. InvokeAction expects the ID of the model instance that contains the action, the name of the action to invoke (which is the UI Action's ActionID attribute value), and a collection of name/value pairs to pass (optional).

(function (container, modelInstanceId) {
	function formUpdatedHandler(args) {
		var isHousehold = container.getFieldByName("ISHOUSEHOLD", modelInstanceId), memberCount;
		if (isHousehold.value) {
			memberCount = container.getFieldByName("HOUSEHOLDMEMBERCOUNT", modelInstanceId);
			if (memberCount.value > 0) {
				container.invokeAction(modelInstanceId, "SELECTHOUSEHOLDMEMBERACTION");
			}
			else {
				container.invokeAction(modelInstanceId, "ADDHOUSEHOLDMEMBERACTION");
			}
		}
     	}
     	container.on("FormUpdated", formUpdatedHandler);
})();

The second variable named modelInstanceID will be to get the ID of the model instance on the server that contains your form fields. You'll always need the model instance ID to reference form fields, since it’s possible that a form can have more than one model instance (in the case of a data form extension, for example).

Everything interesting in your code, such as updating a field's value in your model, executing an action in your model, synching your UI when other fields on the form update the model, etc., are going to depend on this container object.

Tip: If you look at FormContainer within the BBUI technical reference, you'll see the various available methods you can call to update the data form's model.

Set and Retrieve a Field's Value Programmatically

To update a field value programmatically, use the form container's updateField() function:

	container.updateField(modelInstanceId, "MYFIELD", "value");

To get a field's current value or other property at any time, use the container's getFieldByName() function:

	var myField = container.getFieldByName("MYFIELD", modelInstanceId);
	var myFieldValue = myField.value;

Perform Custom Logic When the Model is Updated on the Server

To perform some custom logic when the model is updated on the server, add a handler to the form container's formupdated event:

function formUpdatedHandler(args) {
	// Perform any logic needed to synch your custom UI elements with changes to the model here.
	// This event handler will be called when mapped fields are updated in the model as well as
	// when you update a field's value programmatically using the form container methods.
	var formUpdateResult = args.formUpdateResult;
 
	// To find a particular field in the form update result, use the following code where "MYFIELD"
	// is the name of the field you're looking for:
	var myField = BBUI.forms.Utility.findFieldInFormUpdateResult(formUpdateResult, "MYFIELD", modelInstanceId);
}
container.on("formupdated", formUpdatedHandler);

Other tasks, including invoking a UI action, adding an item to a collection field, etc., are also possible through the container object and follow this same pattern.