Extending SPEasyForms Visibility Rules

This is going to be a fairly short post, because there isn’t that much to extending visibility rules. SPEasyForms visibility rules have two extension points, state handlers and comparison operators. The built-in state handlers and comparison operators in v2014.01 are shown in the two drop down lists on the add/edit visibility rule dialog box shown below:
image
This post is going to explain how to extend the visibility rules by adding the following functionality:
  • Comparison operators for >, >=, <, <=, and !=
  • State handlers to highlight a field in various colors

The first part of the sample code just defines some convenience aliases and utility methods. They’re pretty simple and the comments state their intent so I’m not going to explain them in detail.
// return without doing anything if SPEasyForms has not been loaded
if (!$ || !$.spEasyForms) return;

// shorthand alias for SPEasyForms instances we're going to need
var visibilityRuleCollection = $.spEasyForms.visibilityRuleCollection;
var utils = $.spEasyForms.utilities;

// utility method to extend without overwriting existing properties
if (!utils.extend) {
    utils.extend = function (destination, source) {
        for (var property in source) {
            if (!(property in destination)) {
                destination[property] = source[property];
            }
        }
        return destination;
    };
}

// utility method to determine if a string is a valid date
if (!utils.isDate) {
    utils.isDate = function (value) {
        var date = new Date(value);
        return (date instanceof Date && !isNaN(date.valueOf()));
    };
}
Now lets look at adding some comparison operators. First, we define an object instance with a method for each comparison operator we want to add. The name of the method is what will appear in the drop down on the add/edit visibility rule dialog, converted to title case. The methods should take in two parameters, the first being the value of the field and the second being the literal value we’re comparing it to. It should return true or false. Here is my object instance:
// each method will appear in the comparison operator drop down of
// the add/edit visibility rule dialog (in title case)
var comparisonOperators = {
    greaterThen: function (value, test) {
        if (utils.isDate(value) && utils.isDate(test)) {
            return (new Date(value)) > (new Date(test));
        }
        return (value > test);
    },
    greaterThenOrEqual: function (value, test) {
        if (utils.isDate(value) && utils.isDate(test)) {
            return (new Date(value)) >= (new Date(test));
        }
        return (value >= test);
    }
};
Each method is pretty simple. The one wrinkle is that if both parameters appear to be dates I do a date comparison, since depending on your locale a sting comparison of dates is likely to be pretty unsatisfying. I’ve ommitted less than, less than or equal, and not equal just to save space, but they’re implemented in the sample code.
Now that we have our comparison operators written, we need to merge them into SPEasyForm’s comparison operators, which I do like so:
// replace the visibility managers comparison operators with mine, doing it this way
// makes my new operators take a back seat to ones already defined in SPEasyForms proper.
utils.extend(visibilityRuleCollection.comparisonOperators, comparisonOperators);
Note that I didn’t use jQuery’s extend because I specifically want to skip properties in the source that already exist in the destination. This way if SPEasyForms gets upgraded and the new version already has a greater than, it will get used instead of the AddOn’s greater than. If we were to install the extension right now and launch the add/edit visibility rule dialog, we would see a lot more options in the comparison operators drop down.
image
So lets move on to the state handlers. First I add a utility method called highlight, which looks like:
if (!utils.highlight) {
    utils.highlight = function (rowNode, backgroundColor) {
        // if our class hasn't already been added to the head
        if ($("table.ms-formtable").attr("data-visibility" + backgroundColor) !== "true") {
            // add a class to the head that defines our highlight color
            $("head").append("<style>.speasyforms-" + backgroundColor +
                " { background-color: " + backgroundColor + "; }</style>");

            // add an attribute to the form table to indicate we've already added our class
            $("table.ms-formtable").attr("data-visibility" + backgroundColor, "true");
        }

        // add our class to all table cells in the row, also indicate which class was added with
        // data-visiblityclassadded so the visibility manager can undo our changes when state
        // is changing
        rowNode.find("td").addClass("speasyforms-" + backgroundColor).attr(
            "data-visibilityclassadded", "speasyforms-" + backgroundColor);
    };
}
All this method does is add a style element to the head defining a CSS class with our background color. Then it grabs all of the table cell elements in the field row and adds the CSS class to them. Note that it also adds an attribute to the table cell called data-visibilityclassadded, which it sets to the name of the class we added. This way, during state transitions the visibility rule collection can cleanup what we’ve done before invoking the current state.
There are two more things we could do that the visibility collection knows how to cleanup, which are add elements or hide elements. If we add any elements to the form table we should set their data-visibilityadded attribute to true, and if we hide any elements we should set their data-visibilityhidden attribute to true. That’s pretty much all we can safely do in a state handler, add elements, hide elements, or add CSS classes to elements, because that’s all the visibility collection knows how to undo and our state handler gets called when the state transitions to us, not from us.
So my object instance for highlighting state handlers looks like:
// each method will be available in the state drop down of the 
// add/edit visibility rule dialog (in title case)
var stateHandlers = {
    highlightRed: function (options) {
        utils.highlight(options.row.row, "Red");
    },
    highlightYellow: function (options) {
        utils.highlight(options.row.row, "Yellow");
    },
    highlightGreen: function (options) {
        utils.highlight(options.row.row, "SpringGreen");
    },
    highlightBlue: function (options) {
        utils.highlight(options.row.row, "Aqua");
    }
};

// replace the visibility managers state handlers with mine, doing it this way
// makes my new operators take a back seat to ones already defined in SPEasyForms proper.
utils.extend(visibilityRuleCollection.stateHandlers, stateHandlers);

Once again, we need to merge our state handlers into SPEasyForms state handlers. And once its installed we should see a lot more states available to us in the add/edit visibility rule dialog:

image

Now we can create visibility rules that look like:

image

And our form should look something like:

image

Now don’t get the wrong idea; I don’t even think this is particularly pretty, it was just an easy demonstration of what can be done with state handlers. That’s all there is to our extension for SPEasyForms Visibility Rules. Visibility rules provides the easiest extension point for SPEasyForms, because there is no UI to code. You don’t have to write a dialog that captures user input and stores it somewhere in the configuration. It’s also the least flexible for the exact same reason. For instance, you could do highlighting as a Field Control Adapter instead, in which case in your dialog box you could ask the user what color they want it to be, and they’d have the whole range of HTML colors available to them in a single adapter implementation instead of just 4 hard coded state handlers. But then you also wouldn’t be able to decide if you should apply highlighting based on some complex Boolean logic like you can with visibility rules. Which makes sense depends on your requirements, but as a general rule if all you want to do is formatting rather than changing the behavior of a field, and you don’t need the formatting to be configurable, a state handler may be the easy way to go. The complete solution can be downloaded with the latest downloads package of SPEasyForms.AddOns from my CodePlex site, as either a ready to deploy WSP or in source code form. Hope you like it!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s