2010-11-04
 
Kategorie
 

Custom elements – Part 4 – Custom Field with validation and communication over HTTP

 

Today, we’ll create another custom field that will provide custom validation—the reason why many custom fields are used. To add some spice to this, the validation will be carried out on an external server.

Using JavaScript, you can send HTTP requests and receive responses asynchronously. Since browser security mechanisms prevent us from leaving the activeforms.com domain, all requests are proxied by the ActiveForms server. As a custom element developer, all you need to do is use the sendRequest method of our epoint_activeforms_customField object.

The function has the following signature: sendRequest(method, URL, params, processResponse). The parameters are as follows:

  • method the type of the HTTP request that will be sent. The allowed values are GET and POST.
  • URL the URL the request will be sent to.
  • params the task parameters object. All attributes of this object are used as parameters of the request with values as per the object. For a POST request, they will be sent as POST parameters; for a GET request, they will be attached to the URL as a queryString (possibly replacing parameters already existing in the URL).
  • processResponse a callBack type function that will be called when the server responds. The function signature should be as follows: processResponse(httpStatus, responseText, reposnseXML), where: httpStatus is the server response HTTP status, responseText is a string that contains the full server response to the HTTP request, responseXML is an XML tree of the server response, if the browser has managed to parse the response as XML code.

For example, if you want to send a request to http://www.nbp.pl/kursy/xml/a214z101103.xml (and download current exchange rates) write:

this.sendRequest('GET', 'http://www.nbp.pl/kursy/xml/a214z101103.xml', null, this.processResponse)

The JS code of ActiveForms will send a request to the ActiveForms server, which will then send the appropriate GET request to the National Bank of Poland (NBP) server. When the NBP server responds using XML, the ActiveForms engine will notify our custom element by calling its function:

processNbpXml: function(httpStatus, responseText, responseXML) {
  var positions = responseXML.getElementsByTagName("position"); 
  for (var i=0;i<positions.length;i++) {
    var position=positions[i];
    var currencyCode = position.getElementsByTagName("currency_code")[0].childNodes[0].data;
    if (currencyCode=="EUR") {
      var r = position.getElementsByTagName("average_rate")[0].childNodes[0].data;
      this.setValue(r);
      this.onChange();
      return;
    }
  }
}

This function assumes that the NBP server returned a valid XML code in a known form. Obviously, it would be better to check the HTTP status we received and handle possible errors.

If you want to write your own validator in a custom element, you need to implement the onValidation method. The function is called by the ActiveForms engine while attempting to go to the next page (like all validations). It is called asynchronously—ActiveForms makes custom elements validate themselves and then queries them for the results of the validation. That is why it is important to notify ActiveForms of a completed validation by calling the method from the parameter:

onValidation: function(callback) {
  // here the validate
  // and decide what is the status of the validation
  // and possibly what message validation
  callback();
}

If you want your validator to reference an external server, you need to write the code in such a way that the ActiveForms engine is notified about the completed validation AFTER the response from that server is received. This, however, can be tricky and is done using an anonymous function:

onValidation: function(callback) {
  this.sendRequest("GET", "http://rss.nbp.pl/kursy/xml2/2010/a/10a010.xml", null,
  function(httpStatus, responseText, responseXML) { 
    var rate=this.processNbpXml(responseXML); 
    if (rate<4) {
      this.validationResult="ok";
      this.validationMessage=null;
    } else {
      this.validationResult="not_ok";
      this.validationMessage="The euro exchange rate over ";
    }
    callback();
  });
},

This code uses the onValidation method to send a request to an HTTP server. The callBack function, which will be called when the server responds, is declared inline. That is why we can reference the callback variable (function) in its body. It is called after the processing is completed, when the result of the validation is known.

If the onValidate function doesn’t call the callback function passed in the parameter for any reason (e.g. the developer’s error or prolonged validation), the ActiveForms engine will announce validation timeout. The custom element will be notified about the timeout by calling its onValidationTimeout function. Now, we can decide what to do when the NBP server doesn’t respond like in the example above. The onValidationTimeout function is called synchronously.

At the end of the custom field validation,the ActiveForms engine queries the validation status and possibly the validation message. The query could be made at two points in time:

  • when the callback function is called at the end of processing in onValidation;
  • immediately after the processing of the onValidationTimout function is finished.

At this point our component has to decide what the final validation result is. If the field contains a correct value, it has to return the ‘ok’ string. If the value is incorrect, it has to return ‘not_ok’.

To sum up, the JavaScript code of our custom field should look as follows:

var epoint_activeforms_customField = {
        
  val: "",
        
  getValue: function() {
    return this.val;
  },

  setValue: function(value) {
    this.val = value;
  },

  clear: function() {
    this.setValue(""); 
  },

  enable: function() {
  },

  disable: function() {
  },

  onValidation: function(callback) {
    this.sendRequest("GET", "http://rss.nbp.pl/kursy/xml2/2010/a/10a010.xml", null,
    function(httpStatus, responseText, responseXML) { 
      var rate=this.processNbpXml(responseXML); 
                    
      if (rate<4) {
        this.validationResult="ok";
        this.validationMessage=null;
      } else {
        this.validationResult="not_ok";
        this.validationMessage="Wrong rate";
      }
      callback();
    });
  },

  getValidationResult: function() {
    return this.validationResult;
  },

  getValidationMessage: function() {
    return this.validationMessage;
  },

  onLoad: function() {
  },

  onValidationTimeout: function() {
    validationResult = "not_ok";
    validationMessage = "During the validation error";
  },
        
  getFieldNodes: function() {
    return [];
  },
        
  processNbpXml: function(responseXML) {
    var positions = responseXML.getElementsByTagName("position"); 
    for (var i=0;i<positions.length;i++) {
      var position=positions[i];
      var currencyCode = pozycja.getElementsByTagName("currency_code")[0].childNodes[0].data;
      if (currencyCode=="EUR") {
        var r = position.getElementsByTagName("average_rate")[0].childNodes[0].data;
        return parseFloat(r);
      }
    }
    return null;
  }
}
 
 
recent posts
3rd Apr 2014
Faster than ever!
5th Dec 2013
Live validation!
2nd Dec 2013
Features distilled
12th Mar 2012
Flicking Channels
26th Jan 2012
Your Own Error Page
9th May 2011
Box Properties
15th Apr 2011
Grouping Fields
13th Apr 2011
Form Versioning
24th Feb 2011
Form Access Modes
22nd Feb 2011
Required Fields
4th Feb 2011
Stats!