Component types and classes

Component types

Components typically need to be connected to other components before they can perform a variety of tasks, e.g. a button must be connected to another component before it can do something useful. Each of those components will have one or more references to other components that are expected to be of a specific type. e.g. an event handler component references a component that is an event target, a list view has references to a model, a renderer and a list cell layout factory.

Each component can implement a number of types. A named component class defines a component type of the same name that is an extension of the types that it implements. An anonymous class does not define a component type and not all component types are implemented as component classes. Some component types are simply just names for a specified set of client-side functionality, e.g. methods, properties. A component type can inherit from multiple other types, and because the types are interfaces, a type can be inherited more than once.

On the server a type is identified by its name which consists of a namespace and a local name. In a document it is represented and resolved as an xs:QName. On the client a type corresponds to a specific client-side interface.

Note:

Custom types must not be created using the Volantis namespaces.

A component class

A cx:component-class element defines the structure of all components of a particular type, specifically the references it has to other components.

A class definition provides the following information:

A cx:component-class element can contain mcs:script elements to include either script policies or an inline script. As usual, script policies are treated as libraries and so will only be added to the output document once irrespective of how many times they are used. On the other hand inline scripts will be added each time they are used and therefore it is the page author's responsibility to ensure that they are only included in the input page when needed.

A client-side representation

A cx:client-class element provides information about the client-side JavaScript representation of a component class.

References

A cx:reference element defines the following characteristics about a reference that a component has to another component:

A cx:client-reference element provides information about the client-side representation of a reference. At present this is restricted to how to store the result of resolving the reference on the client-side. If this is not specified, then by default the reference is stored in a field with the same name as the reference. There are three possible ways in which a reference can be stored in the JavaScript object representation of the component: as a field, as an observable property, or through a one argument setter.

Component instances

Each component instance consists of the following:

A component instance can also provide some client-side functionality:

Features

Support for the custom component markup is represented by the following feature:

Exported Features

cf2:CustomComponents

Imported Features

cf2:ObservablePropertySet

Example

<html xmlns="http://www.w3.org/2002/06/xhtml2"
  xmlns:cf2="http://www.volantis.com/xmlns/2009/07/cf2"
  xmlns:cst="http://www.volantis.com/xmlns/2009/07/cf2/template"
  xmlns:cx="http://www.volantis.com/xmlns/2010/07/cf2/cx"
  xmlns:mcs="http://www.volantis.com/xmlns/2006/01/xdime/mcs"
  xmlns:my="http://www.mycomponents"
  xmlns:ui="http://www.volantis.com/xmlns/2009/07/cf2/ui"
  xmlns:car="http://www.cars/">
  <head>
    <title>Custom Component Example</title>
    <meta property='cf2:validation-mode' about='#PoorDriver'>strict</meta>
    
    <!--
     ! This page makes use of Observable Property Prototype and so should explicitly indicate
     ! that by providing an mcs:script element that references an mscr file that imports those
     ! features. Instead this relies on the ui:button element importing the required feature
     ! into the page. This is probably not the best way but is allowed as a button is
     ! explicitly defined as importing that feature.
     !-->
    
    <!--
     ! Car Class
     !-->
    <cx:component-class defines="reference-types" name="car:Car" id="Car">
      <mcs:script type="text/javascript">
        // ==================================================
        // JavaScript representation of Car
        // ==================================================
        var Car = V$.Class.create();
        Car.mixin(V$.cf2.OPSet);
        V$.cf2.OPProto.add(Car, "owner", {});
        Car.methods({
          initialize: function() {
            V$.cf2.OPProto.init(this);
          },
          setEngine: function(engine) {
            this.engine = engine;
          },
          start: function() {
            // Start engine.
          }
        });
      </mcs:script>
      
      <cx:client-class name="Car" start-method="start"/>
      
      <cx:reference name="engine" structural-relationship="child"
        component-types="car:Engine">
        <cx:client-reference setter="setEngine"/>
      </cx:reference>
      
      <cx:reference name="steeringWheel" structural-relationship="child"
        component-types="car:SteeringWheel">
        <cx:client-reference field="steeringWheel"/>
      </cx:reference>
      
      <cx:reference name="owner" structural-relationship="parent"
        component-types="car:Owner">
        <cx:client-reference property="owner"/>
      </cx:reference>
      
    </cx:component-class>
    
    <!--
     ! Engine class
     !
     ! An abstract class (no JavaScript implementation), that defines an API that other engines
     ! implementations have to follow. At the moment there is no way within the component class
     ! to define the API (in terms of methods, parameters, etc. However, future enhancements
     ! may add that support.
     !-->
    <cx:component-class name="car:Engine" defines="reference-types"/>
    
    <!--
     ! Diesel Engine class
     !
     ! A concrete implementation of an engine.
     !-->
    <cx:component-class name="car:DieselEngine" implements="car:Engine"
      defines="reference-types" id="Diesel">
      <mcs:script type="text/javascript">
        // ==================================================
        // JavaScript representation of Diesel Engine
        // ==================================================
        var DieselEngine = V$.Class.create();
        DieselEngine.methods({
          initialize: function(name) {
            this.name = name;
          }
        });
      </mcs:script>
    </cx:component-class>
    
    <!--
     ! Steering Wheel Class
     !
     ! A concrete implementation of a steering wheel.
     !-->
    <cx:component-class name="car:SteeringWheel" defines="reference-types" id="SteeringWheel">
      <mcs:script type="text/javascript">
        // ==================================================
        // JavaScript representation of Steering Wheel
        // ==================================================
        var SteeringWheel = V$.Class.create();
        SteeringWheel.methods({
          initialize: function(color, texture) {
            this.color = color;
            this.texture = texture;
          }
        });
      </mcs:script>
    </cx:component-class>
    
    <!--
     ! Car Owner Class
     !
     ! A concrete implementation of a car owner.
     !-->
    <cx:component-class name="car:Owner" defines="reference-types">
      <mcs:script type="text/javascript">
        // ==================================================
        // JavaScript implementation of Owner
        // ==================================================
        var Owner = V$.Class.create();
        Owner.mixin(V$.cf2.OPSet);
        V$.cf2.OPProto.add(Owner, "car", {});
        V$.cf2.OPProto.add(Owner, "name", {});
        Owner.methods({
          initialize: function(name) {
            V$.cf2.OPProto.init(this);
            this.setName(name);
          }
        });
      </mcs:script>
    </cx:component-class>
    
  </head>
  <body>
    
    <!--
     ! Steering Wheel Instance
     !-->
    <cx:component component-id="PinkFluffySteering" class-ref="SteeringWheel">
      
      <!--
       ! Must specify in each component instance how to construct a steering wheel as no
       ! name was specified on the cx:client-class element.
       !-->
      <cx:client-create>
        return new SteeringWheel("pink", "fluffy");
      </cx:client-create>
    </cx:component>
    
    <!--
     ! Engine instance
     !-->
    <cx:component component-id="Zetec" class-ref="Diesel">
      
      <!--
       ! Must specify in each component instance how to construct a diesel engine as no
       ! name was specified on the cx:client-class element.
       !-->
      <cx:client-create>
        return new DieselEngine("zetec");
      </cx:client-create>
    </cx:component>
    
    <!--
     ! Poor refers to how much money the driver has to spend on cars, not the quality of
     ! their driving. A poor driver is one that can only have at most a single car.
     !-->
    <cx:component id="PoorDriver">
      
      <!--
       ! An anonymous (no name, no id) class.
       !
       ! The encapsulating component does not specify class-ref attribute so it is bound to
       ! this class.
       !-->
      <cx:component-class defines="reference-types" implements="car:Owner">
        
        <!--
         ! Car references are optional - due to min-occurs = 0
         !-->
        <cx:reference name="car" structural-relationship="child" component-types="car:Car"
          min-occurs="0">
          <cx:client-reference setter="setCar"/>
        </cx:reference>
      </cx:component-class>
      
      <cx:client-create>
        // Create the client side representation of a poor driver.
        return new Owner("Willy");
      </cx:client-create>
      
      
      <cx:component id="CompanyCar" class-ref="Car">
        <cx:ref name="engine" component="Zetec"/>
        <cx:ref name="steeringWheel" component="PinkFluffySteering"/>
        <!--
         ! Owner is implicitly bound to containing component. 
         !-->
        
        <cx:client-start>
          // Make sure that the car owner name is appropriate to the steering wheel.
          c.setOwner('Shirley');
        </cx:client-start>
      </cx:component>
      
    </cx:component>
    
    <div>
      <cst:value property-value="PoorDriver#name" path="."/> has a
      <cst:value property-value="PoorDriver#car" path="steeringWheel.color"/>
      <cst:value property-value="PoorDriver#car" path="steeringWheel.texture"/>
      steering wheel and a <cst:value property-value="PoorDriver#car" path="engine.name"/> engine!
      <div>
        <ui:button>
          <cf2:on event="cf2:activate" set="PoorDriver#name" string="Fred"/>
          Rename to Fred
        </ui:button>
      </div>
    </div>
    
  </body>
</html>

Related topics