Yet Another Service Portal Form Field Type

“In every kind of endeavor, there are ample opportunities for extra effort. Grab those opportunities, embrace that extra effort and transform ordinary mediocrity into bright and shining excellence.”
Ralph Marston

Just when I start thinking that I have finally come up with all of the different Form Field Types than I could possibly use, something comes along to make me realize that I could maybe use just one or two more. While building the Button/Icon Editor widget for my Content Selector Configuration Editor, I came across a need for a field that would allow me to select an Icon. I already had a pop-up Icon selection widget, but there wasn’t a form field type that would allow me to configure that as a selection source. I thought about just coding the HTML myself and not bothering with trying to use the snh-form-field element, but then it occurred to me that if I was going to go to all that trouble, I might as well make the changes to the form field provider and release a new version. The work effort seemed to be roughly equivalent, so here we go.

To begin, we need to add the new type to the list of supported field types up at the top. We will call our new field type icon, and just add it to the existing list in its place in alphabetical order.

var SUPPORTED_TYPE = ['checkbox', 'choicelist', 'date', 'datetime-local', 'email', 'feedback', 'icon', 'inlinemultibox', 'inlineradio', 'mention', 'month', 'multibox', 'number', 'password', 'radio', 'rating', 'reference', 'select', 'tel', 'text', 'textarea', 'time', 'url', 'week'];

My plan is to make the input field read-only and only accept input from the pop-up icon picker. To launch the icon picker, I plan to put a search icon at the end of the input element, much like the icons for the url, email, and tel field types. In fact, the code will be so similar, I think I will add the type to the list of special types, which will drive the processing into the section that builds the layout for those types. Currently, that code looks like this:

var SPECIAL_TYPE = {
	email: {
		title: 'Send an email to {{MODEL}}',
		href: 'mailto:{{MODEL}}',
		icon: 'mail'
	},
	tel: {
		title: 'Call {{MODEL}}',
		href: 'tel:{{MODEL}}',
		icon: 'phone'
	},
	url: {
		title: 'Open {{MODEL}} in a new browser window',
		href: '{{MODEL}}" target="_blank',
		icon: 'pop-out'
	}
};

In addition to adding the icon type to the list, I am also going to add an additional property to each type configuration to enable conditional application of the read-only option. For the new icon type, that value will be set to ‘ readonly=”readonly”‘, and for all other types, it will just be an empty string. This way, I can insert the type-specific value into the input element definition without having to have any conditional logic in that part of the process. The updated version of the SPECIAL_TYPE variable value now looks like this:

var SPECIAL_TYPE = {
	email: {
		title: 'Send an email to {{MODEL}}',
		href: 'mailto:{{MODEL}}',
		icon: 'mail',
		extra: ''
	},
	icon: {
		title: 'Select an Icon',
		href: 'javascript:void(0)" ng-click="selectIcon(\'MODEL\');',
		icon: 'search',
		extra: ' readonly="readonly"'
	},
	tel: {
		title: 'Call {{MODEL}}',
		href: 'tel:{{MODEL}}',
		icon: 'phone',
		extra: ''
	},
	url: {
		title: 'Open {{MODEL}} in a new browser window',
		href: '{{MODEL}}" target="_blank',
		icon: 'pop-out',
		extra: ''
	}
};

Those of you paying close attention might also have noticed the other little hack used on the new type: the href value terminates the href attribute and begins a new ng-click attribute. We don’t want to link to another page in this instance, so the value will set the href attribute to “javascript:void(0)” and then add an ng-click attribute to call a new function that will launch the icon picker. We’ll have to add that new function to the scope down in the link section of the provider a little later on.

With that now in place, we can scroll down to the buildSpecialTypes function and start hacking that up to accommodate our new type. Right at the top we grab all of the values relevant to the type for which we are currently building, That code looks like this:

var title = SPECIAL_TYPE[type].title.replace('MODEL', model);
var href = SPECIAL_TYPE[type].href.replace('MODEL', model);
var icon = SPECIAL_TYPE[type].icon;

Since we added an additional property to the type configurations, we will want to modify that code to include that new property as well:

var title = SPECIAL_TYPE[type].title.replace('MODEL', model);
var href = SPECIAL_TYPE[type].href.replace('MODEL', model);
var icon = SPECIAL_TYPE[type].icon;
var extra = SPECIAL_TYPE[type].extra;

Now all we need to do is to use that new value in the input element definition that we are building. That’s just a a few lines down and looks like this:

htmlText += "       <input class=\"snh-form-control\" ng-model=\"" + model + "\" id=\"" + name + "\" name=\"" + name + "\" type=\"" + type + "\"" + passThroughAttributes(attrs) + (required?' ng-required="' + required + '"':'') + "/>\n";

Let’s just add the potential readonly attribute at the very end:

htmlText += "       <input class=\"snh-form-control\" ng-model=\"" + model + "\" id=\"" + name + "\" name=\"" + name + "\" type=\"" + type + "\"" + passThroughAttributes(attrs) + (required?' ng-required="' + required + '"':'') + extra + "/>\n";

Well, that was easy! We still don’t have any code to pop up the icon selector just yet, but we’ve done enough to be able to see how it looks, and to see if we have broken anything so far. Let’s add a new field of type icon to our existing form field test page and see how it comes out. In its simplest form, it would look something like this:

<snh-form-field
  snh-model="c.data.icon"
  snh-name="icon"
  snh-type="icon"/>

Now let’s take a quick peek.

First visual test of the new icon field type

Well, it is definitely read-only, but we did not get our icon displayed at the end of the line as I was hoping. It seems that I neglected to take into account the logic that suppresses the icon if there is no valid data in the field. We don’t really need that in this circumstance, and in fact, we absolutely don’t want it, so we need to make another little modification. This was all one line before, but now we only want to insert the part that hides the icon if the field type is not icon.

htmlText += "       <span class=\"input-group-btn\"";
if (type != 'icon') {
	htmlText += " ng-show=\"" + fullName + ".$valid && " + model + " > ''\"";
}
htmlText += ">\n";

Now, let’s take a look.

Second visual test of the new icon field type

Now that’s better. Now we need to add the function that needs to be called whenever someone clicks on the icon. That goes all the way down at the bottom of the provider script.

scope.selectIcon = function(model) {
	scope.spModal.open({
		title: 'Select Icon',
		widget: 'icon-picker',
		buttons: [
			{label: '${Cancel}', cancel: true}
		],
		size: 'sm',
	}).then(function(response) {
		scope.$eval(model + " = '" + response.selected + "';");
	});
};

Since we don’t have any way to pull in spModal at this point, we are going to have to rely on the client script in the widget to include spModal in their function arguments and attach spModal to the $scope. We already did something similar with snMention, so this will be essentially a copy of that same approach. Here it is on the form field test page, where we have both:

function FormFieldTest($scope, snMention, spModal) {
	var c = this;
	$scope.snMention = snMention;
	$scope.spModal = spModal;
	c.data.picker = {};
}

Now we can take one more look at our test page to see how things will work:

First functional test of the new icon field type

Sweet! Clicking on the search icon pops up the icon picker and clicking on one of the icons on the list populates the form field and closes the picker. And now we have yet one more type of form field. Just like that. I’ll do a little more testing, just to make sure that we did not miss anything, and then I will round up all of the parts and pieces and put them into a new Update Set.