More Service Portal Form Fields

“Code reuse is the Holy Grail of Software Engineering.”
Douglas Crockford

One of the thing that I like about making parts is that, even after you’ve “completed” your work and put a part on the shelf, you can always go back at some point later on and make it even better. When I first set out to create my form field tag, my primary goal was to save myself some work and to set things up so that things would always come out consistently. Consistency is a nice by-product of reusing the same component over and over again. People like it when things are consistent. So when I came across a need for a field type that I had not built into the current version of my form field tag, my first impulse was to pull it off the shelf, dust it off, and give it a bit of an upgrade.

What I needed was a feedback field, which is really a combination to two separate fields, a rating widget and a comments box. There are all kinds of rating widgets out there where you can configure stars or happy faces or some other graphic to indicate some level of satisfaction, but none of the existing field types in my current form field implementation supported that feature, and none of them included a single label for two input elements. What I wanted to do was to support something like this:

Example feedback entry

For the rating, I peeked under the hood of the Knowledge Article widget, and found the uib-rating tag, which looks like it comes from here. That looked like just the thing that I needed, so I all that was left for me to do was to wrap my labels and decorations around that widget in the same manner as I had for the sn-record-picker and sn-choice-list tags. The code for the stand-alone rating was pretty much just a copy, paste, and slightly modify:

if (type == 'radio' || type == 'inlineradio') {
	htmlText += buildRadioTypes(attrs, name, model, required, type);
} else if (type == 'select') {
	htmlText += buildSelect(attrs, name, model, required);
} else if (SPECIAL_TYPE[type]) {
	htmlText += buildSpecialTypes(attrs, name, model, required, type, fullName, label);
} else if (type == 'reference') {
	htmlText += "      <sn-record-picker field=\"" + model + "\" id=\"" + name + "\" name=\"" + name + "\"" + passThroughAttributes(attrs) + (required?' required':'') + "></sn-record-picker>\n";
} else if (type == 'choicelist') {
	htmlText += "      <sn-choice-list sn-model=\"" + model + "\" id=\"" + name + "\" name=\"" + name + "\"" + passThroughAttributes(attrs) + (required?' required':'') + "></sn-choice-list>\n";
} else if (type == 'rating') {
	htmlText += "      <uib-rating ng-model=\"" + model + "\" id=\"" + name + "\" name=\"" + name + "\"" + passThroughAttributes(attrs) + (required?' required':'') + "></uib-rating>\n";
} else ...

For the combination of a rating and a comment block, things got a little more complicated. In all of my other field types, I passed through to the input element all of the original attributes that did not have some other purpose in rendering out the entire block of code. For the first time, I had more than one input element, as I was combining the rating doodad with the textarea for the comments all under one label. After experimenting with different ways to distinguish attributes for one of the elements from attributes for the other, I decided against making things more complicated than they needed to be, and just assume that all non-standard attributes would be attributed to the textarea. Once that was settled, the resulting code turned to be the following:

...
} else if (type == 'rating') {
	htmlText += "      <uib-rating ng-model=\"" + model + "\" id=\"" + name + "\" name=\"" + name + "\"" + passThroughAttributes(attrs) + (required?' required':'') + "></uib-rating>\n";
} else if (type == 'textarea' || type == 'feedback') {
	if (type == 'feedback' && attrs.snhRatingModel) {
		var max = 5;
		if (attrs.snhRatingMax && parseInt(attrs.snhRatingMax) > 0) {
			max = parseInt(attrs.snhRatingMax);
		}
		htmlText += "      <uib-rating ng-model=\"" + attrs.snhRatingModel + "\" max=\"" + max + "\"/>\n";
	}
	htmlText += "      <textarea class=\"snh-form-control\" ng-model=\"" + model + "\" id=\"" + name + "\" name=\"" + name + "\"" + passThroughAttributes(attrs) + (required?' required':'') + "></textarea>\n";
} else {
	htmlText += "      <input class=\"snh-form-control\" ng-model=\"" + model + "\" id=\"" + name + "\" name=\"" + name + "\" type=\"" + type + "\"" + passThroughAttributes(attrs) + (required?' required':'') + "/>\n";
}

As you can see, I did break down and allow for the max attribute by creating an snh-rating-max attribute that would not be passed in to the textarea, but other than that, the rating paired with the comments ends up basically unconfigurable. I may change that one day, but for now, this was all I needed to get me what I was after.

Anyway, I have done a little testing, and it all seems to work so far. If you would like to play around with it on your own, here is an Update Set with all of the relevant parts and pieces.