Custom components

Common information

You can easily create a custom component from any existing one, by using the dhx.protoUI() command.

In short, the technique for creating is the following:

dhx.protoUI({
   name:"mylist", // the name of a new component
   $init:function(config){
       //config - configuration options
       //a custom constructor code
   }
   ... any custom methods can be defined here ...
}, dhx.ui.list); //the name of the base component which the new component inherits from

and later you can use it in a layout as any normal component:

dhx.ui({
    rows:[
         { view:"mylist", data:"some.json", ... },
          ...
    ]
})

Main methods and properties

Before we describe how the mentioned technique works, it helps to summarize the main methods and properties you may use while developing a component.

Properties:

  • $view - the HTML (<div>) component container.
  • $width - the width of the component container.
  • $height - the height of the component container.
  • $ready - an array of commands. You may use this property to call some function as soon as the $init method and all the setters are executed. The functions are added to the array through the command push (call this command in the method $init):
    this.$ready.push(...);

Methods:

  • defaults - defines default settings of the component.
  • $init - for the most part defines component initialization. Called at the very beginning.

    In this method you add HTML elements, specify event handlers. As a parameter the method takes the object 'config' that contains all the properties which a user set in the constructor. Inside the method you can refer to the HTML object of the component through this.$view.
    Note, the method doesn't take properties set in the method 'defaults'. They are available just after initialization is finished completely (i.e. $init and all the property setters are executed).
  • $getSize - defines the size of the whole component. You should use this method to set for the component size differing from the size allocated by the parent view. Called when inner code inquires about height or width which should be set for the component.

    The method returns the array [gravW;width;gravH;height] (width gravity, fixed width, height gravity, fixed height).

    The values of the array conform to the following rules:
    • Width(height) can take the next values:
      • any natural number;
        This value is used as width(height) of the component.
      • -1;
        This value tells the script to ignore the parameter.
    • GravW(gravH) can take any natural value. The value '1' tells the script to ignore the parameter.
    • When width(height) is set to any natural value, gravW(gravH) is ignored. When width(height) is set to -1, the script asks for gravW(gravH) value.

      The possible scenarios:
      • Sc.1 You want the parent container to set the size automatically (autosizing).
        • Solution:
          • don't redefine the function or return [1;-1;1;-1]
      • Sc.2 You want to set relative width(height).
        • Solution:
          • for relative width return [yourValue;-1;1;-1]
          • for relative height return [1;-1;yourValue;-1]
      • Sc.3 You want to set fixed width(height)
        • Solution:
          • for fixed width return [1;yourValue;1;-1]
          • for fixed height return [1;-1;1;yourValue]
  • $setSize - defines behaviour and sizes of the specific elements of the component. You should use this method if your component has a complex structure. Called as soon as the size of the whole component is set (after $getSize method).

    Let's consider some existing component, for example, window. It has a header and a body. Assume, you hide the header. The body of the window will keep the previous size but instead of the header you'll see empty space. To prevent this, you should specify a logic defining behaviour of the elements in such the situation. The appropriate logic must be specified in the method $setSize.

Step-by-step example

As an example, let's create a simple component shown in the image below (the first row of the first column). Maybe, it doesn't have any practical use but it's a good way to explain creating a custom component.


The full code of the example

Our future component can be characterized by the following:

  1. It contains 3 columns of the same width;
  2. Whatever parent container is, the columns save their proportions;
  3. The component completely fills the space allocated to it;
  4. Each column has its html content (for example, text label);
  5. By default, the component has the height of 100 px.

You can apply the following steps while developing the component:

  1. Choose the most appropriate existing classes, components which functionality you want to inherit;
  2. Choose a name for the new component;
  3. Define initialization code for the component ('$init');
  4. Specify the appropriate properties (functions) of the component;
  5. Set the initial height of the component ('defaults', '$getSize');
  6. Define the behaviour of the specific elements in case the parent container will change its size ('$setSize' ).


Step 1. Choosing a component to inherit from

For our component we chose the following, the most appropriate classes:

  • dhx.MouseEvents - to support mouse events, such as onItemClick.
  • dhx.EventSystem - to support event handling functionality, such as attachEvent() method.
  • dhx.ui.baseview - the base class from which all the existing DHTMLX components inherit.

So, the second parameter of the dhx.protoUI() command will be dhx.MouseEvents, dhx.EventSystem, dhx.ui.baseview.

dhx.protoUI({ /*.. component definition...*/}, dhx.MouseEvents, dhx.EventSystem, dhx.ui.baseview);

Step 2. Name your component

From this action we will start defining the component. We'll take the name 'custom'.

dhx.protoUI({
   name:"custom", 
   ...
}, dhx.MouseEvents, dhx.EventSystem, dhx.ui.baseview);

Step 3. Component initialization

$init function answers for initialization.
In our case it will have the following code:

$init:function(config){
	this.$view.className = "my_control";// sets css class for the component
	this.$view.innerHTML = "<div class='item1'></div><div class='item2'></div><div class='item3'></div>";// specifies the appearance of the component
 
}

Step 4. Properties of the component

At this step we must decide which properties our component should have.
We will define 3 properties:

  • item1 - (string) the HTML content of the first column;
  • item2 - (string) the HTML content of the second column;
  • item3 - (string) the HTML content of the third column.
item1_setter:function(value){
	if(value)
		this.$view.childNodes[0].innerHTML = value;
	return value;
},
item2_setter:function(value){
	if(value)
		this.$view.childNodes[1].innerHTML = value;
	return value;
},
item3_setter:function(value){
	if(value)
		this.$view.childNodes[2].innerHTML = value;
	return value;
}

Step 4. Setting the default height

We have 2 variants to set the initial height of the component:

  1. specify in the defaults method the height property;
  2. use $getSize method.

Let's consider 2 variants.

1. height (as well as width) is a keyword and if you set this property in the defaults, the parser will recognize and process it.

defaults:{
	height:100
}

2. But if you use in the defaults the other name it won't be recognized and processed. In such a case you will need to use the method $getSize as well.
So, in the defaults we will specify some property (you can refer to this property later through this.config.[someProperty]) and through $getSize set the initial height to its value.

defaults:{
	controlHeight:100
},
$getSize:function(){
	return [1,-1,0,this.config.controlHeight];
}

Step 6. Adjusting sizes of elements

The parent container can change its size and it will influence on proportions of the columns of our component. To provide the same width of the columns in any situation we will redefine the method $setSize.

$setSize:function(x,y){ 
	if (dhx.ui.view.prototype.$setSize.call(this,x,y)){
		var itemWidth = Math.round(this.$width/3);
		for(var i=0;i<3;i++){
			this.$view.childNodes[i].style.width = (i==2?this.$width-(itemWidth*2):itemWidth) +"px";
			this.$view.childNodes[i].style.height = this.$height+"px";
		}
	}
}

dhx.ui.view.prototype.$setSize.call(this,x,y) - the $setSize method of the parent container. We called it to set size of the whole component.

The method $setSize is called each time when the script sets or changes the size and returns true (size was set) of false (size was changed). As we want to redefine the method just in case of changing size - we'll put it into if block.

The full code of the example