Service Portal Form Fields, Part V

“There is nothing impossible to him who will try.”
Alexander the Great

After doing a lot of testing and tweaking, I decided that it was time to add a few more options to my list of supported types. Everything up to this point was still a basic text or textarea variation that didn’t require any special coding to produce the INPUT element. Now it is time to try something a little more sophisticated that will require a little bit of extra work. Both radio buttons and select statements work off of a list of choices from which you select your value, so I came up with yet another attribute to support types of this nature. I called it snh-choices, and the expectation is that you will provide a JSON array of label/value pairs in the order in which you want them to be presented. Then I added three new values to my list of supported types, choicelist, radio, and inlineradio. The only difference between radio and inlineradio is that the radio type lists the choices from top to bottom and the inlineradio type lists them from left to right. Other than that, they are essentially the same thing.

To support the new types, I created a couple of functions to render the appropriate INPUT element and the associated choices:

function buildRadioTypes(attrs, name, model, required, type) {
	var htmlText = "      <div style=\"clear: both;\"></div>\n";

	var option = null;
	try {
		option = JSON.parse(attrs.snhChoices);
	} catch(e) {
		console.log('Unable to parse snh-choices value: ' + attrs.snhChoices);
	}
	if (option && option.length > 0) {
		for (var i=0; i<option.length; i++) {
			var thisOption = option[i];
			if (type == 'radio') {
				htmlText += "      <div>\n  ";
			}
			htmlText += "        <input ng-model=\"" + model + "\" id=\"" + name + thisOption.value + "\" name=\"" + name + "\" value=\"" + thisOption.value + "\" type=\"radio\"" + passThroughAttributes(attrs) + (required?' required':'') + "/> " + thisOption.label + "\n";
			if (type == 'radio') {
				htmlText += "      </div>\n";
			}
		}
	}

	return htmlText;
}

function buildChoiceList(attrs, name, model, required) {
	var htmlText = "      <select class=\"snh-form-control\" ng-model=\"" + model + "\" id=\"" + name + "\" name=\"" + name + "\"" + passThroughAttributes(attrs) + (required?' required':'') + ">\n";

	var option = null;
	try {
		option = JSON.parse(attrs.snhChoices);
	} catch(e) {
		console.log('Unable to parse snh-choices value: ' + attrs.snhChoices);
	}
	htmlText += "        <option value=\"\"></option>\n";
	if (option && option.length > 0) {
		for (var i=0; i<option.length; i++) {
			var thisOption = option[i];
			htmlText += "        <option value=\"" + thisOption.value + "\">" + thisOption.label + "</option>\n";
		}
	}
	htmlText += "      </select>\n";

	return htmlText;
}

To test out these new types, I modified my little test page yet again to add some additional form fields to try out each of the three new types. The HTML for that page now looks like this:

<snh-panel title="'${Form Field Test}'">
  <form id="form1" name="form1" novalidate>
    <div class="row">
      <div class="col-xs-12 col-sm-6">
        <snh-form-field
          snh-form="form1"
          snh-model="c.data.firstName"
          snh-name="firstName"
          snh-label="First Name"
          snh-type="text"
          snh-required="true"
          snh-messages='{"required":"You must enter a First Name"}'/>
        <snh-form-field snh-form="form1"
          snh-model="c.data.middleName"
          snh-name="middleName"
          snh-label="Middle Name"
          snh-type="text"
          snh-required="false" />
        <snh-form-field
          snh-form="form1"
          snh-model="c.data.lastName"
          snh-name="lastName"
          snh-label="Last Name"
          snh-type="text"
          snh-required="true"
          minlength="5"/>
        <snh-form-field
          snh-form="form1"
          snh-model="c.data.email"
          snh-name="email"
          snh-label="Email Address"
          snh-type="email"
          snh-required="true"
          snh-messages='{"required":"You must enter an Email address", "email":"You must enter a valid Email address"}'/>
      </div>
      <div class="col-xs-12 col-sm-6">
        <snh-form-field
          snh-form="form1"
          snh-model="c.data.textarea"
          snh-name="textarea"
          snh-label="Text Area"
          snh-type="textarea"
          maxlength="20"
          snh-help="This is where the field-level help is displayed."/>
        <snh-form-field
          snh-form="form1"
          snh-model="c.data.choicelist"
          snh-name="choicelist"
          snh-label="Choice List"
          snh-type="choicelist"
          snh-required="true"
          snh-choices='[{"value":"1", "label":"Choice #1"},{"value":"2", "label":"Choice #2"},{"value":"3", "label":"Choice #3"},{"value":"4", "label":"Choice #4"}]'/>
        <snh-form-field
          snh-form="form1"
          snh-model="c.data.radio"
          snh-name="radio"
          snh-label="Radio"
          snh-type="radio"
          snh-required="true"
          snh-choices='[{"value":"1", "label":"Choice #1"},{"value":"2", "label":"Choice #2"},{"value":"3", "label":"Choice #3"},{"value":"4", "label":"Choice #4"}]'/>
        <snh-form-field
          snh-form="form1"
          snh-model="c.data.inlineradio"
          snh-name="inlineradio"
          snh-label="In-line Radio"
          snh-type="inlineradio"
          snh-required="true"
          snh-choices='[{"value":"1", "label":"Choice #1"},{"value":"2", "label":"Choice #2"},{"value":"3", "label":"Choice #3"},{"value":"4", "label":"Choice #4"}]'/>
      </div>
    </div>
    <div class="row">
      <div class="col-sm-12">
        <snh-form-field
          snh-form="form1"
          snh-model="c.data.comments"
          snh-name="comments"
          snh-label="Comments"
          snh-type="textarea"/>
      </div>
    </div>
  </form>
</snh-panel>

And here is the screen shot of the latest version of the test page with the examples of the three new field types:

Form field tester with choice list and radio fields

Of course, that still leaves a lot to be tested, and even more that could be added, but I am starting to think that we are finally at that point where there is more work that has been completed than there is left to be done. Still, there are quite a few items that still remain on the needs to be done list:

  • Fix the way validation messages are displayed when the message changes, but the valid state does not,
  • Figure out how to obtain the name of the form in the template function so that it doesn’t have to be passed in as an attribute,
  • Test all of the field types that are specified in the list of valid field types,
  • Add even more choices to the list of valid field types, and
  • Revisit the form-field-extras DIV that we left out, and see what we might be able to with that feature if we brought that back into play.

As far as additional types go, I was thinking about seeing what I could do with the sn-record-picker, but that sounds like it could be its own challenge. That’s an AngularJS provider as well, and I’m not exactly sure how that would work with a provider inside of a provider. But I guess that’s how you find those things out. But I think I better stick with some of the other loose ends that I’ve skipped over before I start out on yet another experiment into the unknown. Although, it would be interesting to try …