Kauri Documentation
 PreviousHomeNext 
10.2.2 Creating the formBook Index10.3.1 Custom Formatters

10.3 Building Custom Controls

Before you start

Before venturing into creating own/custom controls for the kauri-framework we advise to

  1. Get an understanding of how the system works (read about the concepts, this document, the hackerguide and maybe even the actual code)
  2. Scan what is already there (both references and code) to make sure that what you need is not yet available. Maybe you'll find a close match from which your development can start.

Analyse The Value

The first aspect to consider is deciding which kind of value (type, structure) the control is expected to be editing.

Things to consider:

Write down the structure and a few samples, and note down the different "formats" of this value. I.e. the 'user-format' (i.e. its representation to the end-user) and the 'wire-format' (i.e. the representation-format sent in the json structure to-and-from the data-service)

Note that the user-format only is relevant when the user-interface actually foresees a string-representation of the value that is in some way end-user facing. This includes feedback-only purposes where the user-format is only read, never entered by the user.

Taking the sample of a date:

Understand how this 'value' step is also instrumental in the decision if you really should make a custom control or not:

If you can't come up with a specific value to be edited, then that probably is because there is no value at all. In such cases a kauri-control is not what you are looking for and you should probably investigate into creating some custom User Interface effects through applying jQuery or jQuery-UI stuff and investigate into blending that with your form-controls.

Formatters and Validators

Related to this value-structure you might want to evaluate if the control you envision requires any custom formatters or validators to ensure a correct (and guided) entry of new values.

To get up to speed with creating those we advise to read:

Responsibilities

The core job of your custom control is to manage the bridge between the HTML elements on the page (showing feedback to the user, and receiving his/her input) and the actual typed value kept inside the control.

The control base-class to start from does many of this already in a correct and consistent way. What makes your custom control 'different' however will be the custom look and interaction possibilities it will offer. That is what you will need to focus on.

Value Access

The first aspect to cover is how your control should deal with the value as being entered and fed back to the end-user. This is done through two methods:

  1. void setUserValue(value)
    This should take the passed string-value (it will already be formatted to string by the user-formatter) and use that to update the UI elements so it shows the new value.
  2. String getUserValue()
    This needs to read the (string) value as entered by the end user a return it to the caller.

To do so, both methods should typically operate on the 'input' element (@kauri-role) found in the HTML DOM/tree and associated to the control (@kauri-idref)

Standard initialization will make a reference to that element (jquery-wrapped) available through the getElement() method.

HTML Template Binding and/or Element Creation.

In fact that standard initialization process will have automatically found (or created) all associated HTML elements (i.e. the ones with the different roles as well).

For each kauri-role, this algorithm will use the 'select' and 'create' hints found in the 'elements' property of your control:

  elements[role] = {
     "select" : jQuery-selector-string,
     "create" : jQuery-creator-string (used when selector doesn't find the elements)
  }

After initialization all the found or created elements are available through the 'getElement(role)' method. (When omitted the role for the retrieved element is assumed to be 'input')

After this initial element-binding has happened the initElements(create) method will be called. In this method any additional lookups, element creation or other HTML/DOM initialization can occur. The passed argument 'create' is a boolean indicating if the control is operating in 'create' mode or not. This mode indicates that no matching 'input' element was found, and that all further not-found role elements are expected to be created by your code.

The 'binding' between control and matching HTML elements needs to work in two ways though. Up to now we've explained about the form looking up (or even creating) associated elements so DOM manipulation on them will make sure the UI reflects state changes are reflected in those elements.

The binding however also needs to work the other way around: when the user interacts with the HTML (i.e. mostly <form><input>) elements, the control should be triggered to perform value-parsing, validation, etc etc.

Therefore your control should register event-listeners with these elements. The default initialization already takes care of the change() event on the 'input' element associated to the control, but if you have other HTML elements to track, then you should use the initEvents() method to get that organized.

We advise using the jQuery event mechanism for registering your listeners.

Exposing events

The control itself will publish a 'valueChanged' event (which is distinct from the more low level HTML 'change' event). As we use the jQuery event mechanism for this as well, we 'tunnel' the event through the actual 'input' element of the control.

Your control might want to expose specific events when certain state-changes occur. By convention we advise to use the same 'input' element to do so. Depending on your case either initElements() or initEvents() will be the most logical place to put this standard snippet of code to introduce your own "myEvent"

      this._makeEventHandler("myEvent", optionalBindDataObject);

Triggering the event is done with:

       this.getElement().myEvent();

Or with the more verbose variant from jQuery in case you want to pass optional eventData

       this.getElement().trigger("myEvent", optionalEventData);

Registering a listener will typically happen from outside:

       var siblingControl = this.lookupControl('../sibling-name');
       this.getElement().myEvent(function() {
           // handling the event 
       } );

Event-handlers will be executed in the context of the $input element (i.e. the this reference will point to the control's input element to which the event is bound) and be passed two arguments:

  1. an Event Object (see jQuery documentation). 
  2. a so-called event-data object holding the merger of the optionalBindData and optionalEventData objects.  If not overwritten in the event-data, this will hold a reference to the control in evt.data.control itself.
Control Event Wiring

The form will initialize all its containing controls in an order that matches the logical top-down structure of the composing members. It is important to understand that this order is likely not to be in sync with the (rather arbitrary) dependency-sequence of different controls listening to each other's events.

More clearly: your control's own initElements() and initEvents() methods might be called well before those of the control emitting events it wants to listen to.

To avoid the issue where one is registering for an event that has not been exposed yet, a specific initEventWiring() method is foreseen on your control. This method is assured to be called only after all controls in the form have gone through their respective initElements() and initEvents().

Wrapping up:

  1. Event-declaration code should go into initElements() or initEvents().
  2. While initEventWiring() should be used to register your event-handler on some other control.

Coding

The framework offers an easy base-class for your controls to derive from. Below you find the typical (and minimal) frame of your own custom control.

;
( function( $) {

    var kf = $.org.kauriproject.forms;
    var controlTypes = kf.controlTypes;

The anonymous function construct above is a known technique to ensure no top level naming conflicts will occur. The variables we declare form a shorthand for the namespaces relevant for Kauri.

    $.inherit(MyControl, kf.Control);

    function MyControl( id, form, conf) {
        this['<super.init>'](id, form, conf);
    }

With the $.inherit we declare our control to be a subclass of the base control class.
Your constructor method can just delegate to the base implementation through the <super.init> call.
(One of the features of our prototype-inheritance system)

    MyControl.prototype.elements = {};
    $.extend(MyControl.prototype.elements, kf.Control.prototype.elements);
    $.extend(MyControl.prototype.elements, {
        "some-role" : {
            create :"<div class="my-control">html to create control element for role "some-role"</div>",
            select :"div.my-control"  // jQuery selector to find the some-role element without @kauri-role: if possible
        }
    });

Above you see how to inherit the base-class 'elements' and add specific element directives for your control.

    MyControl.prototype.initElements = function(create) {}

    MyControl.prototype.initEvents = function() {}

    MyControl.prototype.initEventWiring = function() {}
    

These steps in the control initialization have been explained above.
As well as the required user-value handling code below.

    MyControl.prototype.getUserValue = function() {

        var $input = this.getElement();
 
        // logic to read the value entered by the user from the HTML element

        return value; // should typically be a string
    }

    MyControl.prototype.setUserValue = function( value) {

        value = value || "";
        var $input = this.getElement();

        // logic to update the HTML element so it shows to the user what the current value is
    }


    kf.controlTypes.put("my-control", MyControl);

Finally the above line will register your new control which makes it available for referencing it from the fconf.

})(jQuery);

Internally our code uses exactly the same way of working, so check up on the already provided base-controls for more complete and complex examples.

 PreviousHomeNext 
10.2.2 Creating the form10.3.1 Custom Formatters