Service Portal Form Fields, Part II

“The journey of a thousand miles begins with a single step.”
— Lao Tzu (Tao te Ching)

Last week, I had this idea, and today I think I have a fairly good plan. I am going to make a copy of my old sn-panel hack and make myself another Angular Provider for a generic form field and all of the associated trimmings. My strategy, as usual, is to start out small see what I can get to work, and then build it up piece by piece until it finally does everything that I want it to do. I’ll use the tag snh-form-field to invoke the provider, and then I will create my own snh-* attributes and snh-* styles, just to be sure that I don’t conflict with anything already existing in the platform. Ultimately, I want to be able to support any kind of valid HTML5 input, but to start with, I’m going to limit the initial version to just text and textarea fields. At this point, I don’t want to drive things off of the dictionary; I see that as something that would live outside of this process. Everything will be driven off of the attributes specified in the tag, and if I ever do add something driven off of the dictionary, it will simply leverage this work and use the dictionary to determine what the attribute values should be.

To begin, let’s start out with the basic template. This is pretty much the heart and soul of the entire operation, so might as well start out with the basic HTML framework to be used for all types of input. Here is my first attempt at the essential code needed to produce the simplest of structures to include all of the standard elements:

var htmlText = '';
var name = attrs.snhName;
var model = attrs.snhModel;
var type = attrs.snhType || 'text';
type = type.toLowerCase();
if (SUPPORTED_TYPE.indexOf(type) == -1) {
	type = 'text';
}
var required = attrs.snhRequired && attrs.snhRequired.toLowerCase() == 'true';
htmlText += "    <div id=\"element." + name + "\" class=\"form-group\">\n";
htmlText += "      <div data-type=\"label\" id=\"label." + name + "\" nowrap=\"true\">\n";
htmlText += "        <label for=\"" + name + "\" class=\"col-xs-12 col-md-4 col-lg-6 control-label\">\n";
htmlText += "          <span id=\"status." + name + "\"";
if (required) {
	htmlText += " ng-class=\"" + model + ">''?'snh-required-filled':'snh-required'\"";
}
htmlText += "></span>\n";
htmlText += "          <span title=\"" + attrs.snhLabel + "\" data-original-title=\"" + attrs.snhLabel + "\">" + attrs.snhLabel + "</span>\n";
htmlText += "        </label>\n";
htmlText += "      </div>\n";
if (attrs.snhHelp) {
	htmlText += "    <div id=\"help." + name + "\" class=\"snh-help\">" + attrs.snhHelp + "</div>\n";
}
if (type == 'textarea') {
	htmlText += "      <textarea class=\"form-control\" ng-model=\"" + model + "\" id=\"" + name + "\" name=\"" + name + "\"" + (required?' required':'') + "></textarea>\n";
} else {
	htmlText += "      <input class=\"form-control\" ng-model=\"" + model + "\" id=\"" + name + "\" name=\"" + name + "\" type=\"" + type + "\"" + (required?' required':'') + "/>\n";
}
htmlText += "      <div id=\"message." + name + "\" class=\"snh-error-msg\" ng-show=\"" + model + "ErrorMsg>''\">{{" + model + "ErrorMsg}}</div>\n";
htmlText += "     </div>\n";
return htmlText;

The first thing that you see is that we pull some commonly used variables out the associated attributes, mainly for the sake of clarity and brevity. The first real line of HTML is the outer enclosing DIV of everything else, which we name in accordance with the standard as “element.” plus the “name” of the field. The next line is the outer enclosing DIV of field label, which we name in accordance with the standard as “label.” plus the “name” of the field. Next is the LABEL tag, which doesn’t have a name, followed by the SPAN for the required marker, which we name in accordance with the standard as “status.” plus the “name” of the field. After that comes the SPAN for the field label, which also doesn’t have a name, but uses the snh-label attribute for the title as well as the value. Following the label SPAN is the closing LABEL tag and then the closing DIV tag for the field label elements, completing the field label block.

Just below the field label there is an optional DIV for field-level help. You don’t see that all that much, but it is an option on ServiceNow forms, so I wanted to be sure to include it. After that comes the INPUT element itself, the formatting of which is entirely dependent on the type of input. For now, just to have something to start with, I am only supporting two different types, text and textarea. We’ll add more later, but this will be enough to get us started. After the INPUT element, we have the DIV for validation errors, and then we wrap everything up with the closing DIV tag for the outer enclosure. That completes the basic HTML structure for a form field.

To try it out, we’ll need a simple widget with a few form fields defined. Just to see how things come out, let’s do a few text fields and a few textarea fields, make some required and some not required. And just for fun, let’s wrap the whole thing in the old snh-panel.

<snh-panel rect="rect" title="'${Form Field Test}'">
  <form role="form" id="form1" ng-init="checkInputs()">
    <div class="row">
      <div class="col-xs-12 col-sm-6">
        <snh-form-field snh-model="c.data.firstName" snh-name="firstName" snh-label="First Name" snh-type="text" snh-required="true" />
        <snh-form-field snh-model="c.data.middleName" snh-name="middleName" snh-label="Middle Name" snh-type="text" snh-required="false" />
        <snh-form-field snh-model="c.data.lastName" snh-name="lastName" snh-label="Last Name" snh-type="text" snh-required="true" />
      </div>
      <div class="col-xs-12 col-sm-6">
        <snh-form-field snh-model="c.data.textarea" snh-name="textarea" snh-label="Text Area" snh-type="textarea"/>
      </div>
    </div>
    <div class="row">
      <div class="col-sm-12">
        <snh-form-field snh-model="c.data.comments" snh-name="comments" snh-label="Comments" snh-type="textarea"/>
      </div>
    </div>
  </form>
</snh-panel>

Of course, we have to build a Portal Page so that we have somewhere to put our widget, but that’s all basic ServiceNow stuff that we don’t need to go into here. Let’s just assume that we did all that and get right to the good stuff, which is to see how it all comes out when you bring up in the browser.

First Form Field Test

Not bad! The required marker actually works pretty cool … the minute that you type a single letter into the field, it goes from red to grey. I’m used to that changing once you leave the field, but this does it right then and there while you are entering the data. The form doesn’t validate just yet, but hey — it’s a start. I think I am going to really like this once it has all been put together. Next time, we will add a little more and see just how much further we can get …