Overview

CaptainCasa Enterprise Clients provides the possibility to enhance the set of existing components in a flexible way.

We differentiate between two types of components:

Composite components

The “Composite Components” have no implication on client side – they are server-side only. The way to implement is the creation of “Page Bean Components”. Page bean components are defined as normal dialogs – having a layout definition with code that is bound.

Page bean components are directly supported in the Layout Editor by using the component PAGEBEANCOMPONENT.

Please check the Developer's Guide for more information on page bean components.

Client components

The client side of CaptainCasa is a rendering engine that is written in JavaScript. The rendering engine uses a component library that is developed by CaptainCasa following the so called “RISC-method”.

Bringing in new client components means that you really add some new optical components to the JavaScript client – and only is done, if there is no existing component provided inside the CaptainCasa control library.

This is the central topic of this documentation.

Setting up an own component library

Create a CaptainCasa project

Create a just normal CaptainCasa project.

Nothing special here... - just create a project in the way you prefer: directly through toolset, via Maven, ...

Create the component library files

In the CaptainCasa tools: start function “Create control library extension...” from the project menu:

In the dialog that shows up you need to define a prefix for your components and a package in which your component implementation is placed. Example:

After pressing “Create” some files will be generated within your project. Let's take a look onto the files:

Check your project's classpath definition

If your project was created through the CaptainCasa toolset (and not by Maven) then you typically have to add a “jsp-api.jar” library to the classpath of the project within your IDE (e.g. Eclipse), so that the IDE is able to resolve all classes.

Result

Now the component library is “ready to go”! There are no components inside yet... - so the next chapter explains how to add components.

Adding components - The server-side...

The graphical component is a quite simple one – at the beginning. A rectangle area on the screen that can be colored.

The name of the component is “coloredrect” - so together with the prefix of the component library, that was created in the previous chapter, it is “test:coloredrect”.

Define the component and its attributes

The core definition is done in “cccomponentlibrary.xml”. Here we add the following definition:

<?xml version="1.0" encoding="UTF-8"?>

 

<taglib prefix="test"

        clientprefix="test"

        packagename="some.test">

 

    <tag>

        <name>coloredrect</name>

        <attribute><name>id</name></attribute>

        <attribute><name>rectcolor</name></attribute>

        <attribute><name>height</name></attribute>

        <attribute><name>width</name></attribute>

    </tag>

 

</taglib>

 

Please follow the rules:

Add meta information

In “controlattributeusage.xml” you need to define meta information for the component attributes.

<controlattributeusage>

 

    <tag name="test:coloredrect">

        <!-- mandatory attributes -->

        <attribute name="background"/>

    </tag>

    

</controlattributeusage>

 

The most important information is to define the mandatory attributes. This information is used within the Layout Editor of CaptainCasa in order to indicate to the user, where a component instance is not properly configured.

Define the “important” attributes

In “controlfavoriteattributes” you may define these attributes which show up as “important” attributes in the Layout Editor within the attributes-area.

<controlfavoriteattributes>

 

    <tag name="test:coloredrect">

        <attribute name="background"/>

    </tag>

    

</controlfavoriteattributes>

 

Define the “arrangement” of your component

The Layout Editor needs to know where your component can be placed within a layout definition – so that the user can pick it via the right mouse button menu that is available in the tree of componets.

The corresponding definition is done in “controlsarrangement.xml”:

<controlsarrangement>

 

    <tag name="t:row" below="test:coloredrect"/>

    <tag name="test:coloredrect" folder="Perfect test components"/>

    

</controlsarrangement>

For new attributes – Add some text and valid values

The attribute “rectcolor” of the new component is not contained in the default attribute names that are used by CaptainCasa. So you may add further information, which is showing up in the Layout Editor when editing the attribute.

The definition in “controlattributeinfo.xml” is:

<controlattributeinfo>

    <attribute name="rectcolor" text="The background color of the rect-area.">

        <attributevalue value="#FF0000" text="Red"/>

        <attributevalue value="#00FF00" text="Green"/>

        <attributevalue value="#0000FF" text="Blue"/>

    </attribute>

</controlattributeinfo>

 

Implement the Component and the ComponentTag class

For each component you need to define two classes – one “ComponentTag”-class and one “Component”-class. You can just copy the existing “YOURCOMPONENTComponent”-class and the exsiting “YOURCOMPONENTComponentTag”-class.

The name of your classes must exactly follow the pattern:

package some.test;

 

import org.eclnt.jsfserver.elements.BaseActionComponent;

 

public class COLOREDRECTComponent

    extends BaseActionComponent

{

}

 

package some.test;

 

import org.eclnt.jsfserver.elements.BaseComponentTag;

 

public class COLOREDRECTComponentTag

    extends BaseComponentTag

{

    public void setRectcolor(String value)   

    {

        m_attributes.put("rectcolor",value);

    }

}

 

The “ComponentTag” class requires a set-method for each attribute of the component. Because it is extended from “BaseComponentTag”, all the existing CaptainCasa attribute names are already inherited – so you do not have to take care of the attributes “id”, “widht”, “height”. But the attribute “rectcolor” is not part of CaptainCasa attributes, so you have to add it.

Result

After building your project, reloading your application in the Layout Editor and refreshing the Layout Editor, you now can see the new component being part of the editing environment:

You can edit the component attributes...

...but you do not see any component in the preview area of the page right now – because there is still some client implementation missing!

Adding components - The client-side...

Writing the JavaScript code

The JavaScript is by default placed into the “extension.js” file. This file is automatically loaded by the browser if a project is using your component library.

The JavaScript code requires you to write two classes (functions):

The JavaScript code of “coloredrect”

/*

* Component implementation.

*/

function TESTCOLOREDRECTControl(asPrototype)

{

    this.init_TESTCOLOREDRECTControl = function()

    {

        this.init_RISCComponent();

        this.setStyleClass("riscpane");

        this.setBackgroundColor("#0000FF");

    };

    this.calculateMinimumSize = function()

    {

        return new RISCDimension(50,50);

    };

    this.setRectColor = function(value)

    {

        this.setBackgroundColor(value);

    };

    this.layoutChildren = function(left,top,width,height)

    {

    };

    // -------------------------------------------------------------------------

    if (asPrototype != true) this.init_TESTCOLOREDRECTControl();

}

TESTCOLOREDRECTControl.prototype = new RISCComponent(true);

 

 

/*

* Element implementation.

*/

function TESTCOLOREDRECTElement(asPrototype)

{

    this.init_TESTCOLOREDRECTElement = function()

    {

        this.init_CCControl();

        this.applyComponentData = this.applyComponentData_TESTCOLOREDRECTElement;

        this.m_ccClassName = "TESTCOLOREDRECTElement";

        // --------------------------------------------------------------------

        var vControl = new TESTCOLOREDRECTControl();

        this.bindUI5Node(vControl);

    };

    this.applyComponentData_TESTCOLOREDRECTElement = function()

    {

        this.applyComponentData_CCControl();

        if (this.m_attributesChanged["rectcolor"] == true)

        {

            this.m_ui5Node.setRectColor(this.m_attributes["text"]);

        }

    };

    if (asPrototype != true) this.init_TESTCOLOREDRECTElement();

}

TESTCOLOREDRECTElement.prototype = new CCControl(true);

s_CCPageElement_ElementCreators["test_coloredrect"] = function() { return new TESTCOLOREDRECTElement(); };

Result

Please always remember:

Finally the result looks as follows:

The component is rendered and is following the configuration in the layout definition.

The client-side JavaScript framework

Documentation resources

There is an API documentation of the client side components. Please open the following link:

http://captaincasa.com/docu/api_clientjs

JavaScript source code

The source code of the client side is available through a public GIT repository. Please contact CaptainCasa (info@CaptainCasa.com) in order to obtain access.

Two JavaScript classes to develop

For each client-side component you need to develop two classes. (We talk here about “classes” - actually these are JavaScript “functions” - but used in the meaning of classes.)

The “Element” class

Overview

The server-side of CaptainCasa sends an XML layout description to the client-side JavaScript processing. This layout description is very similar to the XML layout definitions (i.e. the XML inisde the .jsp pages) – but considers certain runtime aspects:

On client side the XML layout description is transferred into an hierarchy of “Element” objects – each element representing one element of the XML.

The purpose of the element objects is to receive the attributes from the XML data, to create a UI-component out of it and to transfer changing attribute values to the component. Vice versa the element object receives events from its UI-component and transfers changed data and event data back to the server-side processin.

The element object as consequence is the translator between what happens on the XML communication side and the actual graphical components.

Implementation

Let's take a look onto the implementation of the “test:coloredrect” example...

function TESTCOLOREDRECTElement(asPrototype)

{

    this.init_TESTCOLOREDRECTElement = function()

    {

        this.init_CCControl();

        this.applyComponentData = this.applyComponentData_TESTCOLOREDRECTElement;

        this.m_ccClassName = "TESTCOLOREDRECTElement";

        // --------------------------------------------------------------------

        var vControl = new TESTCOLOREDRECTControl();

        this.bindUI5Node(vControl);

    };

    this.applyComponentData_TESTCOLOREDRECTElement = function()

    {

        this.applyComponentData_CCControl();

        if (this.m_attributesChanged["rectcolor"] == true)

        {

            this.m_ui5Node.setRectColor(this.m_attributes["text"]);

        }

    };

    if (asPrototype != true) this.init_TESTCOLOREDRECTElement();

}

TESTCOLOREDRECTElement.prototype = new CCControl(true);

s_CCPageElement_ElementCreators["test_coloredrect"] = function() { return new TESTCOLOREDRECTElement(); };

 

...and let's walk through step by step:

function TESTCOLOREDRECTElement(asPrototype)

{

    ...

}

TESTCOLOREDRECTElement.prototype = new CCControl(true);

The name of the class is “TESTCOLOREDRECTElement” - which is the concatenation of the prefix, the name of the component and the word “Element”. We strongly recommend to follow this naming convention.

The class is taking over all content from the “CCControl” class – which serves as its prototype.

...

s_CCPageElement_ElementCreators["test_coloredrect"] = function() { return new TESTCOLOREDRECTElement(); };

 

A function to create an instance of this class is registered. This means: if layout-XML is received on the client side and if the XML contains an element “test_coloredrect”, then the corresponding element instance is created by the function that is passed into the global object “s_CCPageElement_ElementCreators”.

Remember that the server-side prefix is prepended with “_” when the control is communicated to the client-side.

function TESTCOLOREDRECTElement(asPrototype)

{

    this.init_TESTCOLOREDRECTElement = function()

    {

        ...

    };

    if (asPrototype != true) this.init_TESTCOLOREDRECTElement();

}

 

If the class/function “TESTCOLOREDRECTElement” is called, and if it is not called as prototyp function, then the init-method of the class is called. The init-method is implemented with the name “init_<celementClassName>” so that it does not conflict with prototype implementations.

    this.init_TESTCOLOREDRECTElement = function()

    {

        this.init_CCControl();

        this.applyComponentData = this.applyComponentData_TESTCOLOREDRECTElement;

        ...

    };

    this.applyComponentData_TESTCOLOREDRECTElement = function()

    {

        this.applyComponentData_CCControl();

        ...

    };

 

In the init-method first the init of the parent/prototype-class is called.

The element class needs to implement three central methods:

You see the function “applyComponentData” is explicitly assigned with the function “applyComponentData_<elementClassName>”. In the implementation of the function “applyComponentData_TESTCOLOREDRECTElement” first the implementation of the parent/prototype class is called.

If the class also manages sub-components, then the same meachnism has to be done for the methods “addChildNode” and “removeChildNode” - example:

    this.init_TESTCOLOREDRECTElement = function()

    {

        this.init_CCControl();

        ...

        this.addChildNode = this.addChildNode_TESTCOLOREDRECTElement;

        this.removeChildNode = this.removeChildNode_TESTCOLOREDRECTElement;

        ...

    };

    this.addChildNode_TESTCOLOREDRECTElement = function(ccControl,index)

    {

        this.addChildNode_CCControl(ccControl,index);

        ...

    };

    this.removeChildNode_TESTCOLOREDRECTElement = function(ccControl)

    {

        this.removeChildNode_CCControl(ccControl);

        ...

    };

 

In our example of the “test:coloredrect” component we do not manages sub-components, to we do not have to to do so.

    this.init_TESTCOLOREDRECTElement = function()

    {

        ...

        var vControl = new TESTCOLOREDRECTControl();

        this.bindUI5Node(vControl);

    };

    this.applyComponentData_TESTCOLOREDRECTElement = function()

    {

        ...

        if (this.m_attributesChanged["rectcolor"] == true)

        {

            this.getUI5Node().setRectColor(this.m_attributes["text"]);

        }

    };

 

In the “init_” method, finally the graphical component is created and passed into the internal processing via the “bindUI5Node(...)” method.

In the “applyComponentData” implementation, the attributes of the element are checked for changes and changed values are transferred to the component. The method “applyComponentData” is called when the element shows up first, and when there are changes to its attributes due to server-side updates.

The “Component” class

Overview

The component class is representing a visible component. It uses the CaptainCasa RISC library for building up corresponding DOM elements in the browser. This CaptainCasa RISC library is a just normal library of classes – in which at runtime component instances are arranged as tree.

The component library in principal is completely separated from the XML/Element-processing that is described in the previous chapter!

Implementation

Let's again use the “test:coloredrect” example...

function TESTCOLOREDRECTControl(asPrototype)

{

    this.init_TESTCOLOREDRECTControl = function()

    {

        this.init_RISCComponent();

        this.setStyleClass("riscpane");

        this.setBackgroundColor("#0000FF");

    };

    this.calculateMinimumSize = function()

    {

        return new RISCDimension(50,50);

    };

    this.setRectColor = function(value)

    {

        this.setBackgroundColor(value);

    };

    this.layoutChildren = function(left,top,width,height)

    {

    };

    // -------------------------------------------------------------------------

    if (asPrototype != true) this.init_TESTCOLOREDRECTControl();

}

TESTCOLOREDRECTControl.prototype = new RISCComponent(true);

 

...and walk through the significant parts:

function TESTCOLOREDRECTControl(asPrototype)

{

    this.init_TESTCOLOREDRECTControl = function()

    {

        ...

    };

    if (asPrototype != true) this.init_TESTCOLOREDRECTControl();

}

TESTCOLOREDRECTControl.prototype = new RISCComponent(true);

 

The class is named “TESTCOLOREDRECTControl” following the convention: “prefix” + “component name” + “Control”. We strongly recommend to follow this convention.

The class takes over all processing from the class “RISCComponent”, which serves as prototype. An init_* method serves as “constructor”. RISCComponent is the most generic control class – providing a rectangular area that can be modified by a lot of properties, and that may have sub-components that are managed inside. (This feature is not used in this example.)

    this.calculateMinimumSize = function()

    {

        return new RISCDimension(50,50);

    };

 

Each component is part of a component tree, inside the component tree there are components (PANE, ROW, ...) that take over the arrangement of sub-components. To do so, these components have to know the minimum size of the sub-components.

    this.setRectColor = function(value)

    {

        this.setBackgroundColor(value);

    };

 

The component provides some property “rectColor” - in this case the property directly calls a property that is implemented by its parent/prototype class.

Adding events to the example

Let's extend the example “test:coloredrect” and extend it in the following way:

Listenering to events in the “Component” class

This time we start with the component class – because this is the one that actually receives the event out of the browser's DOM processing. The code is extended by the highlighted lines:

function TESTCOLOREDRECTControl(asPrototype)

{

    // keep instance of prototype implementation's method

    this.reactOnRISCEvent_parent_TESTCOLOREDRECTControl = this.reactOnRISCEvent;

    this.init_TESTCOLOREDRECTControl = function()

    {

        this.init_RISCComponent();

        this.m_coloredRectListeners = [];

        this.setStyleClass("riscpane");

        this.setBackgroundColor("#0000FF");

        // register for click events => events are passe to the method

        // reactOnRISCEvent

        this.setClickCallback(this);

    };

    this.calculateMinimumSize = function()

    {

        return new RISCDimension(50,50);

    };

    this.setRectColor = function(value)

    {

        this.setBackgroundColor(value);

    };

    this.layoutChildren = function(left,top,width,height)

    {

    };

    this.addColoredRectListener = function(listener)

    {

        this.m_coloredRectListeners.push(listener);

    };

    this.reactOnRISCEvent = function(riscEvent)

    {

        var parentResult = this.reactOnRISCEvent_parent_TESTCOLOREDRECTControl(riscEvent);

        if (parentResult == true)

            return true;    

        if (riscEvent.type == RISCEVENTTYPE_onclick)

        {

            this._processClickedEvent();

            return true; // no further processing, no bubbling

        }         

    };

    this._processClickedEvent = function(riscEvent)

    {

        for (var i=0; i<this.m_coloredRectListeners.length; i++)

            this.m_coloredRectListeners[i]("rectSelected",riscEvent);   

    };

    // -------------------------------------------------------------------------

    if (asPrototype != true) this.init_TESTCOLOREDRECTControl();

}

TESTCOLOREDRECTControl.prototype = new RISCComponent(true);

 

Continuing in the “Element” class

The element class is the one to bind the graphical component to the server side processing. So it's the one to listen to the component's event and to transfer these events which are relevant.

The code now looks as follows:

function TESTCOLOREDRECTElement(asPrototype)

{

    this.init_TESTCOLOREDRECTElement = function()

    {

        this.init_CCControl();

        this.applyComponentData = this.applyComponentData_TESTCOLOREDRECTElement;

        this.m_ccClassName = "TESTCOLOREDRECTElement";

        // --------------------------------------------------------------------

        var vControl = new TESTCOLOREDRECTControl();

        var that = this;

        vControl.addColoredRectListener(function(name,riscEvent) {that._processColoredRectEvent(name,riscEvent);});

        this.bindUI5Node(vControl);

    };

    this.applyComponentData_TESTCOLOREDRECTElement = function()

    {

        this.applyComponentData_CCControl();

        if (this.m_attributesChanged["rectcolor"] == true)

        {

            this.m_ui5Node.setRectColor(this.m_attributes["text"]);

        }

    };

    this._processColoredRectEvent = function(name,riscEvent)

    {

        if ("rectSelected" == name)

        {

            // send action to server side

            var command = "rectSelected()";

            this.getPage().callServer(this,this.getId()+".action",command);

        }

    };

    if (asPrototype != true) this.init_TESTCOLOREDRECTElement();

}

TESTCOLOREDRECTElement.prototype = new CCControl(true);

s_CCPageElement_ElementCreators["demo_coloredrect"] = function() { return new TESTCOLOREDRECTElement(); };

 

Continuing on server-side – Add “actionListener”

Now that your component sends out events, you need to update the configuration of attributes in “cccomponentlibrar.xml” by adding the “actionListener” attribute:

    <tag>

        <name>coloredrect</name>

        <attribute><name>id</name></attribute>

        <attribute><name>background</name></attribute>

        <attribute><name>height</name></attribute>

        <attribute><name>width</name></attribute>

        <attribute><name>actionListener</name></attribute>

    </tag>

Continuing on server-side – Event class

The event of the client triggers a round-trip to the server-side. On server-side the event command is transferred into a Java-event object. There are two possibilities:

The special event class for our example looks like:

package democontrols;

 

import javax.faces.component.UIComponent;

import org.eclnt.jsfserver.elements.BaseActionEvent;

 

public class BaseActionEventRectSelected

    extends BaseActionEvent

{

    public BaseActionEventRectSelected(UIComponent component, String type)

    {

        super(component, type);

    }

}

 

The constructor must exactly provide the two parameters – otherwise the object will not be created.

Result

Now you can bind the actionListener of the component to some Java-method – just as with “normal” controls. The event that will be passed in the actionListener-method will be of type “BaseActionEventRectSelcted”.