Advanced UI Extension Examples

The code that can be added to UI extensions provides nearly endless flexibility. Below are some advanced examples that may come in useful when optimizing the usability of a UI extension.

Scrollable content

It is possible to include a long list of checkboxes in a UI extension. This can be useful, for example, in a survey. To ensure that the rest of the UI extension does not get pushed down too far, it is possible to place the checkboxes in a scrollable list.

The following UI extension demonstrates a method of neatly packing a long list of checkboxes into a confined space. This is done through the CSS by setting the size of the parent <div> and specifying that any overflow (i.e. contents of the div that fall outside of its bounds) should be scrolled to.

HTML

<div class="scroll-me">
  <div class="row vertical checkbox">
    <input id="1" type="checkbox" value="1">
    <label for="1" title="One">One</label>
  </div>
  <div class="row vertical checkbox">
    <input id="2" type="checkbox" value="2">
    <label for="2" title="Two">Two</label>
  </div>
  <div class="row vertical checkbox">
    <input id="3" type="checkbox" value="3">
    <label for="3" title="Three">Three</label>
  </div>
  <div class="row vertical checkbox">
    <input id="4" type="checkbox" value="4">
    <label for="4" title="Four">Four</label>
  </div>
  <div class="row vertical checkbox">
    <input id="5" type="checkbox" value="5">
    <label for="5" title="Five">Five</label>
  </div>
  <div class="row vertical checkbox">
    <input id="6" type="checkbox" value="6">
    <label for="6" title="Six">Six</label>
  </div>
  <div class="row vertical checkbox">
    <input id="7" type="checkbox" value="7">
    <label for="7" title="Seven">Seven</label>
  </div>
  <div class="row vertical checkbox">
    <input id="8" type="checkbox" value="8">
    <label for="8" title="Eight">Eight</label>
  </div>
  <div class="row vertical checkbox">
    <input id="9" type="checkbox" value="9">
    <label for="9" title="Nine">Nine</label>
  </div>
  <div class="row vertical checkbox">
    <input id="10" type="checkbox" value="10">
    <label for="10" title="Ten">Ten</label>
  </div>
</div>
CSS:
.scroll-me {
  height: 95px;
  overflow-y: scroll;
}
.label_checkbox {
  width: 460px;
  overflow-y: hidden;
}

Note that this CSS can be applied to any div that needs to be confined to an area of limited height, e.g. a section with multiple fields or a disclaimer text.

One checkbox is required

The following UI extension uses JavaScript to prevent the user from checking more than one checkbox and requiring a minimum of one checkbox to be checked. This can be useful, for example, in a survey.

HTML

<div class="section" id="laptop_selection">
<span class="section-header">Laptop Selection</span>

<div class="row vertical checkbox">
  <input id="laptop1" type="checkbox" checked>
  <label for="laptop1">Small laptop<br></label>
  <p> </p>
</div>

<div class="row vertical checkbox">
  <input id="laptop2" type="checkbox">
  <label for="laptop2">Medium laptop<br></label>
  <p> </p>
</div>

<div class="row vertical checkbox">
  <input id="laptop3" type="checkbox">
  <label for="laptop3">Large laptop<br></label>
  <p> </p>
</div>

</div>

JavaScript
var $ = ITRP.$;            // jQuery
var $extension = $(this);  // The UI extension container with custom HTML

var $checkboxes1 = $('#laptop_selection :checkbox');
$checkboxes1.on('change', function() {
  var $checkbox = $(this);
  if ($checkbox.is(':checked')) {
    $checkboxes1.not('#' + this.id).prop('checked', false);
  } else {
    $checkbox.prop('checked', false);
  }
});

var $checkbox_required = $('#laptop_selection :checkbox');
$checkbox_required.change(function() {
  $checkbox_required.toggleClass('invalid', !$checkbox_required.is(':checked'));
});

This example code results in a list of three checkboxes while only permitting one to be checked. One can add an unlimited number of checkboxes to this design without having to modify the JavaScript.

Minimum and maximum number of checks

This code from the previous example is perfect when only a single box is allowed to be checked. The following UI extension is a modification of the previous example. It allows a minimum and a maximum number of required checks to be specified:

HTML

<div class="section" id="laptop_selection">
<span class="section-header">Select Two Peripherals</span>

<div class="row vertical checkbox">
  <input id="peripheral1" type="checkbox" checked>
  <label for="peripheral1">External Hard Disk<br></label>
  <p> </p>
</div>

<div class="row vertical checkbox">
  <input id="peripheral2" type="checkbox">
  <label for="peripheral2">USB Speakers<br></label>
  <p> </p>
</div>

<div class="row vertical checkbox">
  <input id="peripheral3" type="checkbox">
  <label for="peripheral3">Wireless Headset<br></label>
  <p> </p>
</div>

</div>

JavaScript
var $ = ITRP.$;            // jQuery
var $extension = $(this);  // The UI extension container with custom HTML

minimum = 2;
maximum = 2;

if($('#laptop_selection :checkbox:checked').length < minimum){
  $('#laptop_selection :checkbox').addClass('invalid');
}

$('#laptop_selection :checkbox').change(function() {
  var $checkbox = $(this);
  if($checkbox.is(':checked') && $('#laptop_selection :checkbox:checked').length > maximum){
    $checkbox.removeAttr('checked');
  }
  if($('#laptop_selection :checkbox:checked').length >= minimum){
    $('#laptop_selection :checkbox').removeClass('invalid');
  }else{
    $('#laptop_selection :checkbox').addClass('invalid');
  }
});

The variables minimum and maximum determine the minimum and maximum number of checkboxes that must be checked by the user.

Show field when box is checked

To make it easier to fill out a form, it is best to initially hide all the sections and fields that may not need to be filled out. When a user selects a specific option, additional fields can be presented to collect the information related to the selected option.

In the UI extension below, an event is triggered when a checkboxes is checked. This then causes an additional input field to appear.

HTML

<div class="row vertical checkbox">
  <input id="email" type="checkbox" class="show-when-empty">
  <label for="email">Email</label>
</div>

<div id="email_address_row" class="row vertical">
  <label for="email_address">Email address</label>
  <input id="email_address" type="email" autocomplete="on" autocorrect="off" autocapitalize="off" spellcheck="false" class="required">
</div>

JavaScript
var $ = ITRP.$;            // jQuery
var $extension = $(this);  // The UI extension container with custom HTML

$("#email").change(function() {
  if (email.checked) {
      $("#email_address_row").show(200);
  } else {
      $("#email_address_row").hide();
      $("#email_address").val('').change();
  }
}).change();

Show field when boxes are checked

The following UI extension is similar to be previous example, except that it is triggers an event when multiple checkboxes have been checked. In this example an additional input field becomes available for the user when the user has checked 2 specific boxes. This can be useful, for example, in cases where additional information needs to be collected from the user, but only when a specific combination of checkboxes have been checked.

HTML

<div class="row vertical checkbox">
  <input id="send_info" type="checkbox" class="show-when-empty">
  <label for="send_info">Send more information</label>
</div>

<div class="row vertical checkbox">
  <input id="external" type="checkbox">
  <label for="external" title="Person is not an employee">Person is not an employee</label>
</div>

<div id="email_address_row" class="row vertical">
  <label for="email_address">Email address</label>
  <input id="email_address" type="email" autocomplete="on" autocorrect="off" autocapitalize="off" spellcheck="false" class="required">
</div>

JavaScript
var $ = ITRP.$;            // jQuery
var $extension = $(this);  // The UI extension container with custom HTML

$("#send_info,#external").change(function() {
  if ($("input:checked").length === 2) {
      $("#email_address_row").show(200);
  } else {
      $("#email_address_row").hide();
      $("#email_address").val('').change();
  }
}).change();

The result is that initially only the checkboxes ‘Send more information’ and ‘Person is not an employee’ are visible. When both of these boxes are checked, the input field ‘Email address’ becomes available.

This code can be easily adjusted when more boxes need to be checked before the .change(function() is triggered. It can also be used when a minimum number of options (e.g. 2 out of 3 checkboxes) need to be checked before revealing the additional field(s).

Show field when option is selected

In some cases it can be useful to show an additional field (or a complete section) when a specific option is selected in a select field. The following UI initially only shows the Category field in which the user can select 4 options. Only when the option ‘Pesticide’ is selected will the EPA number field become available.

HTML

<div class="row vertical">
  <label for="category">Category</label>
  <select id="category" class="required">
    <option value=""></option>
    <option value="detergent">Detergent</option>
    <option value="lubricant">Lubricant</option>
    <option value="pesticide">Pesticide</option>
    <option value="preservative">Preservative</option>
  </select>
</div>

<div id="epa_row" class="row vertical">
  <label for="epa">EPA number</label>
  <input id="epa" type="number" autocomplete="off" min="10000" max="100000" step="1" class="required">
</div>

JavaScript
var $ = ITRP.$;            // jQuery
var $extension = $(this);  // The UI extension container with custom HTML

$("#category").change(function() {
  if ($(this).val() === 'pesticide') {
        $("#epa_row").show(200);
    } else {
        $("#epa_row").hide();
        $("#epa").val('').change();
    }
}).change();

It is easy to adjust the example above so that the EPA number field gets displayed when either the option ‘Pesticide’ or ‘Preservative’ is selected. The JavaScript would then look as follows:

JavaScript
var $ = ITRP.$;            // jQuery
var $extension = $(this);  // The UI extension container with custom HTML

$("#category").change(function() {
  if ($('#category').val() === 'pesticide' || $('#category').val() === 'preservative') {
        $("#epa_row").show(200);
    } else {
        $("#epa_row").hide();
        $("#epa").val('').change();
    }
}).change();

Show field when option and checkbox are selected

In some cases it can be useful to show an additional field (or a complete section) when a specific option is selected in a select field. The following UI initially only shows the Category field in which the user can select 4 options. Only when the option ‘Pesticide’ is selected will the EPA number field become available.

HTML

<div class="row vertical">
  <label for="category">Category</label>
  <select id="category" class="required">
    <option value=""></option>
    <option value="detergent">Detergent</option>
    <option value="lubricant">Lubricant</option>
    <option value="pesticide">Pesticide</option>
    <option value="preservative">Preservative</option>
  </select>
</div>

<div class="row vertical checkbox">
  <input id="permit" type="checkbox">
  <label for="permit" title="Permit required">Permit required</label>
</div>

<div id="epa_row" class="row vertical">
  <label for="epa">EPA number</label>
  <input id="epa" type="number" autocomplete="off" min="10000" max="100000" step="1" class="required">
</div>

JavaScript
var $ = ITRP.$;            // jQuery
var $extension = $(this);  // The UI Extension container with custom HTML

$("#category,#permit").change(function() {
  if ($('#category').val() === 'pesticide' && $('#permit').is(':checked')) {
        $("#epa_row").show(200);
    } else {
        $("#epa_row").hide();
        $("#epa").val('').change();
    }
}).change();

Add another set of fields

The following example initially shows one set of input fields. A user can add another set of the same input fields when that is needed. In this example, a user is able to add up to three sets of the same input field. This limit can be modified simply by adding more sections with those same fields.

This can be useful, for example, when users needs to be able to register one or more people for a training.

HTML

<div class="section" id="section1">
  <span class="section-header">Guest 1</span>

  <div class="row vertical">
    <label for="name1">Full name</label>
    <input id="name1" type="text" autocomplete="off" class="required">
  </div>


  <div class="row vertical">
    <label for="date1">Date</label>
    <input id="date1" type="text" autocomplete="off" class="required date">
  </div>

  <div class="row vertical checkbox">
    <input id="password1" type="checkbox">
    <label for="password1">WiFi password required</label>
  </div>
  <a class="hide-order" href="#">Hide</a>
</div>

<div class="section hidden-section" id="section2">
  <span class="section-header">Guest 2</span>

  <div class="row vertical">
    <label for="name2">Full name</label>
    <input id="name2" type="text" autocomplete="off" class="required">
  </div>

  <div class="row vertical">
    <label for="date2">Date</label>
    <input id="date2" type="text" autocomplete="off" class="required date">
  </div>

  <div class="row vertical checkbox">
    <input id="password2" type="checkbox">
    <label for="password2">WiFi password required</label>
  </div>
  <a class="hide-order" href="#">Hide</a>
</div>

<div class="section hidden-section" id="section3">
  <span class="section-header">Guest 3</span>

  <div class="row vertical">
    <label for="name3">Full name</label>
    <input id="name3" type="text" autocomplete="off" class="required">
  </div>

  <div class="row vertical">
    <label for="date3">Date</label>
    <input id="date3" type="text" autocomplete="off" class="required date">
  </div>

  <div class="row vertical checkbox">
    <input id="password3" type="checkbox">
    <label for="password3">WiFi password required</label>
  </div>
  <a class="hide-order" href="#">Hide</a>
</div>

<a id="add-order" href="#">Add another</a>

JavaScript
var $ = ITRP.$;            // jQuery
var $extension = $(this);  // The UI extension container with custom HTML

var $sections = $('.section'),
    $addOrder = $('#add-order');

ITRP.hooks.register('after-prefill', function() {
  $sections.find('.hidden-section').each(function() {
    var $section = $(this);
    if (ITRP.hasFormData($section)) {
      $section.removeClass('hidden-section');
    }
  });
});

$addOrder.click(function(event) {
  event.preventDefault();
  var $hiddenSections = $sections.find('.hidden-section');
  if ($hiddenSections.length === 1) {
    // Last one left, hide the add-order option.
    $(this).hide();
  }
  $hiddenSections.first().removeClass('hidden-section');
});

$('a.hide-order').click(function(event) {
  event.preventDefault();
  var $section = $(this).parent('.section');

  // Hide section + clear form data
  $section.addClass('hidden-section');
  ITRP.clearFormData($section);

  // More sections available, show the add-order option.
  if ($sections.find('.hidden-section').length > 0) {
    $addOrder.show();
  }

});
CSS
.hidden-section { display: none; }

This code may not always be necessary. If, for example, one only requires two sections, this simplified alternate version works as well:

HTML

<div class="section" id="section1">
<span class="section-header">Order 1</span>

<div class="row horizontal">
  <label for="options1" title="Options">Options</label>
  <select id="options1" class="required">
    <option value="value_1_1">Value 1</option>
    <option value="value_1_2">Value 2</option>
    <option value="value_1_3">Value 3</option>
  </select>
</div>

<div class="row horizontal">
  <label for="number1" title="Number">Number</label>
  <input id="number1" type="number" autocomplete="off" min="1" max="20" step="1">
</div>

<a id="toggle-order2" href="#">
  <span class="show-order2">Add another order</span>
  <span class="hide-order2">Remove this order</span>
</a>

<div class="section" id="section2">
<span class="section-header">Order 2</span>

<div class="row horizontal">
  <label for="options2" title="Options">Options</label>
  <select id="options2" class="required">
    <option value="value_2_1">Value 1</option>
    <option value="value_2_2">Value 2</option>
    <option value="value_2_3">Value 3</option>
  </select>
</div>

<div class="row horizontal">
  <label for="number2" title="Number">Number</label>
  <input id="number2" type="number" autocomplete="off" min="1" max="20" step="1">
</div>

</div>
</div>

JavaScript
var $ = ITRP.$;            // jQuery
var $extension = $(this);  // The UI extension container with custom HTML

var $section = $('#section1');
$('#toggle-order2').click(function(event) {
  $section.toggleClass('section2-shown');
  event.preventDefault();
});

Telephone number mask

This UI extension uses JavaScript to transform a 10 digit telephone number taken from a normal text input field into one that accepts phone numbers in the format (XXX)-XXX-XXXX only. This can help users avoid mistakes when entering a telephone number without having to worry about they formatting. This example can be used to prepare similar masks for, for example, zip codes.

HTML

<div class="row horizontal">
  <label for="phone-number" title="Telephone">Telephone</label>
  <input id="phone-number" name="phone-number" type="text" maxlength="14" placeholder="(XXX) XXX-XXXX" autocomplete="off">
</div>

JavaScript
var $ = ITRP.$;            // jQuery
var $extension = $(this);  // The UI extension container with custom HTML

$('#phone-number')

  .keydown(function (e) {
    var key = e.charCode || e.keyCode || 0;
    $phone = $(this);

    // Auto-format- do not expose the mask as the user begins to type
    if (key !== 8 && key !== 9) {
      if ($phone.val().length === 4) {
        $phone.val($phone.val() + ')');
      }
      if ($phone.val().length === 5) {
        $phone.val($phone.val() + ' ');
      }
      if ($phone.val().length === 9) {
        $phone.val($phone.val() + '-');
      }
    }

    // Allow numeric (and tab, backspace, delete) keys only
    return (key == 8 || 
        key == 9 ||
        key == 46 ||
        (key >= 48 && key <= 57) ||
        (key >= 96 && key <= 105));
  })
  
  .bind('focus click', function () {
    $phone = $(this);
    
    if ($phone.val().length === 0) {
      $phone.val('(');
    }
    else {
      var val = $phone.val();
      $phone.val('').val(val); // Ensure cursor remains at the end
    }
  })
  
  .blur(function () {
    $phone = $(this);
    
    if ($phone.val() === '(') {
      $phone.val('');
    }
  });

Retrieving field options from ITRP

The UI extension below retrieves data from ITRP that is used to populate the list of options for two select fields. One field allows a user to select a person, the other allows a configuration item (CI) to be selected.

The JavaScript in this example looks up all the people from the organization that has the ID=52 (which is Widget North America, Manufacturing) and lists the names of these people as options in the Person select field. The JavaScript also looks up all CIs that are linked to a service instance for which the current user is covered by an active SLA. For the CIs the option nameFilter is set to CIs that contain the word ‘printer’ in their name. Note that the filter is applied on the client after the CIs have been retrieved from the server.

This example makes use of the access rights of the user, so there are no security issues.

HTML

<div class="row vertical">
  <label for="person" title="Person">Person</label>
  <select id="person">
    <option value=""></option>
  </select>
</div>

<div class="row vertical">
  <label for="ci" title="CI">CI</label>
  <select id="ci">
    <option value=""></option>
  </select>
</div>

JavaScript
var $ = ITRP.$;            // jQuery
var $extension = $(this);  // The UI extension container with custom HTML

var fillSelect = function($select, url, includeBlank, nameFilter, account) {
  var updateSelect = function(json) {
    var options = includeBlank ? '<option value=""></option>' : '';
    for (var i = 0; i < json.length; i++) {
      if (!nameFilter || nameFilter.test(json[i].name)) {
        options += '<option value="' + json[i].id + '">' + Html.encode(json[i].name) + '</option>';
      }
    }
    $select.html(options);
  };

  var data = {
    url:      url,
    type:     'GET',
    dataType: 'json',
    success:  updateSelect
  };
  if (account) {
    data.headers = {'X-ITRP-Account':account};
  }
  $.ajax(data);
};
// fill Person dropdown using Internal Organization 'Widget North America, Manufacturing'
fillSelect($extension.find('#person'), '/v1/people?organization_id=52&per_page=100', false, null, 'widget');

// fill CI dropdown using name filter on 'printer' (case insensitive)
fillSelect($extension.find('#ci'), '/v1/me/ci_coverages?per_page=100', true, /printer/i);

Date selection restrictions

The following HTML creates an input field that allows a user to select a date.

HTML

<div class="row vertical">
  <label for="start" title="Start">Start</label>
  <input id="start" type="text" autocomplete="off" class=" date">
</div>

There are numerous ways that JavaScript can be added to restrict the dates that a user can select in this date field. A few examples are provided below.

If a user should not be allowed to select a date that is more than 2 days before the creation date of the record, this minimum limit can be defined using JavaScript as follows:

JavaScript
var $ = ITRP.$;            // jQuery
var $extension = $(this);  // The UI extension container with custom HTML

setTimeout(function() {
  var minDate = ITRP.record.initialValues.created_at ? new Date(ITRP.record.initialValues.created_at) : new Date();
  minDate.setDate(minDate.getDate() - 2);
  $('#start').datepick('option', { minDate: minDate });
});

Alternatively, if a user should not be allowed to select a date that is more than 2 days ago (regardless of the record’s creation date), this minimum limit can be defined using JavaScript as follows:

JavaScript
var $ = ITRP.$;            // jQuery
var $extension = $(this);  // The UI extension container with custom HTML

setTimeout(function() {
  $('#start').datepick('option', { 
    minDate: '-2d', 
    enforceMinMaxRestriction: false 
  });
  
  $('#start').on('change', function() {
    $('#start').datepick('option', {
      enforceMinMaxRestriction: true 
    });
  });
});

If the maximum date value is 3 weeks from now, this JavaScript can be used:

JavaScript
var $ = ITRP.$;            // jQuery
var $extension = $(this);  // The UI extension container with custom HTML

setTimeout(function() {
  $('#start').datepick('option', {
    maxDate: '+3w',
    enforceMinMaxRestriction: false 
  });
  
  $('#start').on('change', function() {
    $('#start').datepick('option', {
      enforceMinMaxRestriction: true 
    });
  });
});

In order to add a minimum and a maximum, one can combine the previous JavaScript examples into the following:

JavaScript
var $ = ITRP.$;            // jQuery
var $extension = $(this);  // The UI extension container with custom HTML

setTimeout(function() {
  $('#start').datepick('option', { 
    minDate: '-2d',
    maxDate: '-2d +3w',
    enforceMinMaxRestriction: false 
  });
  
  $('#start').on('change', function() {
    $('#start').datepick('option', {
      enforceMinMaxRestriction: true 
    });
  });
});

If users should only be allowed to select weekdays, the following JavaScript can be included in the UI extension:

JavaScript
var $ = ITRP.$;            // jQuery
var $extension = $(this);  // The UI extension container with custom HTML

setTimeout(function() {
  $('#start').datepick('option', {
    beforeShowDay: $.datepick.noWeekends
  });
});