Skip to content

UI Component - Events

You are able to define the functionality and behavior of the UI Component when the following events occur:

  • Load
  • Field Watch
  • Cleanup
  • Client Preprocessor
  • Server Preprocessor

Load Function

The Component Load function is executed when the Component is first loaded on the form. It effectively initiates the component. Typical tasks that may be performed in the Load function:

  • initialization of values/properties of the Component;
  • setup bindings on the Component with the values defined in a Template's Component Definition entry;
  • injection of Services into the Component to call upon later;
  • applying Watches to values/properties of the Component.

Tip: For those familiar with developing plain AngularJS Directives, the Load Function is the equivalent of using the link function.

The following example is taken from the sc-text-box UI Component:

function (){
   if( typeof scope.tplItem.readOnly === 'undefined' || scope.tplItem.readOnly === null ){
      scope.readOnly=false;
   }else{
      scope.readOnly = scope.tplItem.readOnly; 
   }
    if( !(typeof scope.tplItem.enabled === 'undefined' || scope.tplItem.enabled === null) ){
     scope.readOnly = !scope.tplItem.enabled; 
   }
   if( typeof scope.tplItem.disableSave === 'undefined' || scope.tplItem.disableSave === null ){
     scope.disableSave=false;
   }else{
     scope.disableSave = scope.tplItem.disableSave; 
   }
   var ModifiedFieldService = $injector.get('ModifiedFieldService');

   //This is watched function for readOnly field
   scope.$watch("readOnly", function(value) {
      //logger.info(value,scope.readOnly,element,element.parent());
      if(value){
        $(element).addClass('disabled').addClass('readonly');
        $(element.parent()).addClass('disabled').addClass('readonly');
      }else{
        $(element).removeClass('disabled').removeClass('readonly');
        $(element.parent()).removeClass('disabled').removeClass('readonly');
      }
   }, true);
}

In the above example, scope.tplItem refers to the "Template Item" (ie. the Component definition entry) on the Template being used to build the current form. It's configured details are being read to set the readOnly and disableSave properties of the Component. Next, a Formbird service called ModifiedFieldService is injected into the Component using the AngularJS $injector.get function. This makes ModifiedFieldService available for use within the Component code. Other Services accessible in the Application may also be injected including: $uibModal, $ocModal and $window. Finally, a watch on the "readOnly" property is added to add/remove classes based on the value it is set to.

Tip: Watches may be added in the Load Function in addition to being defined in the Field Watch Function. Always check both locations when working with Watches

Field Watch Function

The watch function executes whenever the value of the field changes in the form. This is typically used to trigger functional behavior within the Component, like setting other fields/properties within the Component. This differs from the OnFieldChange Ruleset functionality available at the Template level which handles the higher-level functional requirements of the field change in the context of the Template and other values on the form.

The following example is taken from the sc-text-box UI Component:

function(newValue, oldValue) {
     if(newValue!==oldValue){
        if(newValue){
        //logger.info('has value this element',element.find('.gbl-textbox-class'));
        $(element.find('.sc-textbox-class')).removeClass('is-empty');
        }else{
        //logger.info('no value this element',element.find('.gbl-textbox-class'));
        $(element.find('.sc-textbox-class')).addClass('is-empty');
        }
        if (scope.disableSave) { 
          ModifiedFieldService.setDirty(!scope.disableSave);
        }
     }
}

In the above example, a change of value triggers jQuery functions to add/remove a Class on the CCS class .sc-textbox-class depending on the presence of a value.

It also calls the ModifiedFieldService.setDirty function if disableSave is true.

Tip: Field Watch functions may also exist in the Load function of the Component. Always check both locations when working with Watches

Cleanup Function

This function performs any cleanup needed on destroy of the component on the form.

Tip: For those familiar with developing plain AngularJS Directives, the Cleanup Function is the equivalent of using the destroy function.

Cleaning up objects used by a third-party library is a common scenario. The following example cleans up Google Places autocomplete data, if present:

function(){
    // Clean up the Google Places autocomplete as it leaves
    // pac-container elements behind that build up and slow the app down
    if (autocomplete) {
        logger.debug("Cleaning up Google Places Autocomplete");

        var obj = autocomplete.gm_accessors_.place;
        $.each(Object.keys(obj), function(i, key) {
            if(typeof(obj[key]) === "object" && obj[key].hasOwnProperty("gm_accessors_")) {
                obj = obj[key].gm_accessors_.input[key];
                return false;
            }
        });
        $.each(Object.keys(obj), function(i, key) {
            if($(obj[key]).hasClass("pac-container")) {
                obj = obj[key];
                return false;
            }
        });
        $(obj).remove();
    }
}

Client Preprocessor Function

The Client Preprocessor function runs pre-save on the client side (when save tick is clicked). It runs before all pre-save rulesets. It can be used to correct data that is required in a pre-save ruleset or run client side component rules or functions.

This function is can be used to correct data used to ensure that the value provided to the client pre-save rules. The returned value is not used so changes made must be done to document[field.name].

Example converting a checkbox value to boolean:

function verifyInput(field, document){

    if(!!document[field.name] && _.isString(document[field.name])){
       if(document[field.name] === "true" || document[field.name] === "false" ){
          document[field.name] = JSON.parse(document[field.name]);
       }else{
          ...
          logger.error("Preprocessing Failure : " + errMsg);
          return;
       }
}

Note: If the preprocessor is asynchronous it needs to return a promise (or use async function ....) but doesn't need to resolve with a value. If it's synchronous it doesn't need a return value. One must also add a flag to the component document: preprocessorIsAsynchronous: true.

Server Preprocessor Function

The Server Preprocessor function performs any necessary tasks prior to saving the value in the system. The function executes on the server-side during the save process.

This function is predominately used to ensure that the value provided to the server for saving is valid and conforms to the expected structure and data type that the Component is expected to capture. This is essential for values that may lose their data type information during submission (eg. Date values are converted to String values when submitted in a JSON structure to the server) or where an external process submits values that by-pass checks that may be inherent within the GUI portion of the UI Component.

The following example is taken from the sc-date-time UI Component:

function(){ 
    var moment = require('moment');
    if (document[field.name]) {
        var saveValue = document[field.name];
        if(Object.prototype.toString.call(saveValue) === "[object Date]" && isNaN(saveValue.getTime()) === false) {
            return document;
        } else if (!isNaN(saveValue)) {
            document[field.name] = new Date(saveValue);
            return document;
        } else {
            // turning on strict parsing with the true at the end of the moment() call 
            var date = moment(document[field.name], ["DD/MM/YYYY hh:mm a", "DD/MM/YYYY", "hh:mm a","DD/MM/YY","DD/MM/YY hh:mm a","DD/MM",moment.ISO_8601], true);
            if (date.isValid()) {
                document[field.name] = date.toDate();
                return document;
            } else {
                var errMsg = field.componentName + " field named " + field.name +
                    " has invalid value \"" + saveValue + "\"";
                logger.error("Preprocessing Failure : " + errMsg);
                return null;
            }
        }
    } else {
        return document;
    }
}

In the above example, the preprocessor tests the data type of the value supplied in document[field.name], and attempts to convert it to a Date Object if it is not one.