Components

Overview

A bundle can declare components to provide instances of JavaScript objects that are created and managed by the App Runtime as needed. Other component instances — declared by the same or a different bundle — can discover and use ("consume") them.

An example

The "map-init" bundle declares a component that provides an instance of a map frame. The "scalebar" bundle declares a component that requires a map frame instance, so it can read the scale of the frame and display the scale in the bottom right corner of a map.apps app.

The App Runtime creates the components and provides the requested map frame instance to the scalebar instance as soon as it becomes available.

In the example above the map frame component provides a service (a map frame instance) while the scalebar component requires a service (a map frame instance). The important thing is that the components have no direct dependencies: the scalebar is satisfied as soon as it gets a map frame, no matter which bundle provides the service.

Component declaration

To declare a component in a bundle, add a component description to the "components" array of the bundle manifest like this:

Component description in manifest.json file
{
  "name" : "zoom",
  "version": "1.0.0",
  "components" : [
    {
      "name": "ZoomSlider",        (1)
      "impl": "ZoomSliderImpl",    (2)
      "provides": "zoom.Control",  (3)
      "properties": {              (4)
        "+fooProperty": "foo",
        "barProperty": "bar"
      }
    }
  ]
}
1 Name of this component. This is the only required property. The name must be unique within this bundle.
2 Name of the implementation class of the component.
3 Identifier for the service provided to other components.
4 List of public and private component properties.

Visit the component configuration reference for a list of all supported properties of a component description.

Implementation class

All components must have a corresponding JavaScript class that implements the component’s behavior. By default the name of the component is used to locate its implementation class.

As an example, if the component "name" is "ZoomSlider", the App Runtime tries to resolve the class name ZoomSlider by looking into the object returned by the module.js file.

If the implementing class has a different class name, add the property "impl" to specify the actual class name like this:

{
  "name" : "ZoomSlider",
  "impl" : "ZoomSliderImpl"
}

Find more details about the class loading mechanism where the bundle layer file is explained.

Providing a service

A component instance is registered as a service in the App Runtime if its component description declares the property provides. The property contains the names of the provided service interfaces, the value can be a string or an array.

{
  "name" : "ZoomSlider",
  "provides" : "zoom.Control"
},
{
  "name" : "ZoomInButton",
  "provides" : ["zoom.Control","zoom.Button"]
}

The properties specified in the components description are also important for service registration, see Component Properties. It is important that each property which does not start with a _ prefix is treated as public service property and registered as service properties.

Instance factories

Sometimes it is required to register a different instance as a service than the one that you want to configure as a component implementation. This is often the case if you have a ready-to-use class which is implemented independent of the component model and you do not want to introduce component system-relevant code inside this class, for example configuration code. A very typical use case is the use of Dijit widgets as components. To support this case the component description has to be marked as "instanceFactory", which means that the component instance acts as factory for the service.

{
  "name" : "ExternalZoomSliderFactory",
  "provides" : ["zoom.Control"],
  "instanceFactory": true
}

A component instance which is marked as an instance factory must implement the method createInstance, which is responsible for creating the "real" service instance. This service instance is registered at the App Runtime. Optionally the component instance can provide a destroyInstance method, which is responsible for cleaning up the service instance during shutdown.

ExternalZoomSliderFactory.js
import ZoomSlider from "./zoomToolsLib";

export default class {
    createInstance() {
        return new ZoomSlider();
    },
    destroyInstance(instance) {
        instance.destroyRecursive();
    }
}

Properties

Properties declaration

A component description can declare properties. These properties are used for two purposes. The first is to provide a configuration mechanism and the second to specify service properties (if the "provides" property is defined).

Components: properties
{
  "name" : "ZoomSlider",
  "provides" : ["myservice.Interface"],
  // properties of the component
  "properties": {
    // underscore properties are private properties
    "_offset" : 125,
    // all other properties are registered as service properties (if provides exist)
    "url" : "http://locahost:8080/..."
  }
}

The recommended way to access component properties within an implementation class is to use the provided property _properties. This is a plain JavaScript object which can be used to access the configuration properties.

Components: injected properties
export default class {
    aServiceMethod(){
      // accessing the injected '_properties' object
      let properties = this._properties;
      // the url property
      let url = properties.url;
      // the offset property
      let offset = properties._offset;
      ...
    }
}

Another way to retrieve the properties is to specify the flag "propertiesConstructor" on the component description. This defines that the configuration properties are transported as constructor options into the implementation class. Here the properties are exactly like the '_properties' property a plain JavaScript object. Besides the transportation as constructor properties, the configuration properties are still injected as _properties property.

Components: propertiesConstructor
{
  "name" : "ZoomSlider",
  "provides" : ["myservice.Interface"],

  // the component implementation shall get the properties as constructor options
  "propertiesConstructor": true,
  "properties": {
    "_offset" : 125,
    "url" : "http://locahost:8080/..."
  }
}
Components: properties constructor access
export default class {
    constructor(properties){
      // the url property
      let url = properties.url;

      // the offset property
      let offset = properties._offset;
      ...
    }
  });
});

Configuration properties are read only. You are not allowed to change them.

Properties visibility

Properties can be public or private. Public properties are part of the component interface and registered as service properties if a component instance is registered as a service.

By default, all properties prefixed with an underscore _ are treated as private properties, while all other properties are treated as public.

You can override the default by prefixing property names with a + to make them public, and - to make them private. If you use the + prefix for at least one property, all other properties are private. Using - as a prefix to make a property private has no effect on the visibility of other properties.

{
    "name": "ZoomSlider",
    "provides" : "anInterface",
    "properties": {
        "+enableFoo": true,
        "-internalProp" : "prop"
    }
}

Types

Immediate component

Immediate component instances are activated immediately as soon as all references are satisfied.

A component is an immediate component if it defined the property "immediate": true and is not a factory component. A component is treated as immediate if it does not declare a "provides" property.

Immediate Component
{
  "name" : "MyComponent",
  "provides" : ["myservice.Interface"],
  "immediate": true
}

If the component has no references, you can consider the lifetime of the component as equal to BundleActivator, which is created on the start of a bundle and destroyed during the stop of the bundle. Only immediate components are allowed to act asynchronously in their activation method. This means that a component instance is allowed to return a dojo.Deferred during its activate() method and the App Runtime waits until activate() finishes before it registers the component instance as a service at the App Runtime.

Delayed component

An instance of a delayed component is not activated until its service is first requested.

A component description which declares a "provides" property and which is not an immediate component and not a factory component is treated as delayed. This means that delayed components are the most common kind of components, because this is the default case if no other special property is declared.

Delayed Component
{
  "name" : "MyComponent",
  "provides" : ["myservice.Interface"]
}

Delayed components are so named because of the fact that service registration is made before the component instance is created. This means that the creation of the component instance is delayed until the first use of the registered service. A delayed component instance is destroyed if it becomes unused. As a result, delayed components can have a very dynamic life because construction and destruction might happen very often and are highly dependent on usage of the service provided by the component.

If a delayed component declares the property "serviceFactory": true, the App Runtime creates a new service instance for each requesting bundle.

Factory component

An instance of a factory component provides a service that generates new component descriptions. This service allows client bundles to create and activate multiple component configurations and dispose of them.

A component description which specifies the property "componentFactory": true is treated as factory component.

Factory Component
{
  "name" : "MyComponent",
  "provides" : ["myservice.Interface"],
  "componentFactory" : true
}

A factory component is a special design pattern, because it acts more as a template for the dynamic creation of component configurations based on component descriptions. The App Runtime parses the component description. If it is satisfied, it registers a service with the interface ct.framework.api.ComponentFactory for this configuration. This service provides a method newInstance, which can be used to create new component configurations during runtime. This newInstance method returns a ComponentInstance object, which provides the method getInstance for accessing the real instance, and the method dispose for shutting down the component configuration. The caller of newInstance is responsible for the disposal of the created component configuration.

Usage of a Factory Component
// Activator using a component factory
export default class{
  start(bundleContext) {
    // search special component factory with name "MyComponentFactory"
    let references = bundleContext.getServiceReferences("ct.framework.api.ComponentFactory","(Component-Name=MyComponent)");
    try {
      // get the component factory service
      let factory = bundleContext.getService(references[0]);

      // create a new component configuration
      // here the default component properties defined in the declaration of the component factory can be overwritten.
      // only properties configurable via json are allowed to be defined here
      let componentInstance = factory.newInstance({ "aproperty": "newValue" });

      // get component/service implementation
      // this feature should rarely be used, better define a reference
      let impl = componentInstance.getInstance();

      impl.doSomething();

      // the created component configuration has to be cleaned up later
      componentInstance.dispose();

    } finally {
      bundleContext.ungetService(references[0]);
    }
  }
}

Factory Components are a very powerful feature because the newInstance method creates new component configurations. This means that such a new dynamically created component configuration is managed by the App Runtime as if it were a new and independent component description in the manifest.json file. Therefore if the property provides is specified, the new component instance is registered as a service in the App Runtime.

Because of the ComponentInstance.getInstance method, a component configuration created by a factory component is treated as an immediate component, therefore, regarding its using state, the component instances are directly created. An important point is that a factory component is satisfied if all references of the component description are also satisfied. In some circumstances this definition leads to unexpected behaviors especially if references are using placeholders in filter expressions.

Usage of a Factory Component in other Component
// Other component using a component factory

// manifest.json
{
   "name" : "OtherComponent",
   "references" : [{
        "name" : "_factory",
        "providing" : "ct.framework.api.ComponentFactory",
        "filter": "(Component-Name=MyComponent)"
    }]
}

// implementation of OtherComponent
export default class {
  activate() {
      // create a new component configuration
      // here the default component properties defined in the declaration of the component factory can be overwritten.
      // only properties configurable via json are allowed to be defined here
      let componentInstance = this._factory.newInstance({ "aproperty": "newValue" });

      // get component/service implementation
      // this feature should rarely be used, better define a reference
      let impl = componentInstance.getInstance();

      impl.doSomething();

      // the created component configuration has to be cleaned up later
      componentInstance.dispose();
  }
}

Lifecycle

Enabled

A component must be enabled before it can be used. You can define the initial value of "enabled" in the component description as property "enabled". The default value is true, so by default components are enabled.

Component enabled property
{
    "name" : "MyComponent",
    "provides" : ["myservice.Interface"],

    // disables the component
    "enabled": false
}

You can programmatically control this state calling enableComponent() and disableComponent() on the ComponentContext. A component can only enable/disable the components within its own bundle.

The next sample shows how a component can be used to enable/disable another component "ToolXY".

programmatic enable/disable
export default class {

        activate(componentContext){
            this._componentContext = componentContext;
        }
        deactivate(){
            this._componentContext = null;
        }

        enableToolXY(){
            this._componentContext.enableComponent("ToolXY");
        }
        disableToolXY(){
            this._componentContext.disableComponent("ToolXY");
        }
}

Satisfied

The App Runtime activates a component configuration only if the component configuration is satisfied. The following conditions must be true for a component configuration to become satisfied:

  • The component is enabled.

  • All the references are satisfied. A reference is satisfied when the reference specifies optional cardinality or there is at least one target service for the reference.

As soon as any of the listed conditions are no longer true, the component configuration becomes unsatisfied and is deactivated.

Lifecycle states

Immediate components

The lifecycle state diagram of an immediate component looks like this:

immediate component states

If an immediate component becomes satisfied, it is activated, and if it provides a declaration, it is registered as a service in the App Runtime. It remains in ACTIVE state until it becomes unsatisfied, which triggers the unregistration and deactivation of the component.

Delayed components

The lifecycle state diagram of a delayed component looks like this:

delayed component states

If a delayed component becomes satisfied, it is first registered as service in the App Runtime. When the first client gets the registered service, the component is activated. The component remains in active state until it becomes unsatisfied or if the last client "ungets" the service instance, which triggers the deactivation of the component. If unget triggered the deactivation, the component remains registered. Otherwise it becomes unsatisfied.

Factory components

The lifecycle state diagram of a factory component looks like this:

factory component states

If a factory component becomes satisfied, the factory service is created and registered at the App Runtime. The factory service is registered until it becomes unsatisfied. If a client calls newInstance on the factory, a new component configuration is created and activated. The lifecycle of a component created by a factory is equal to the lifecycle of an immediate component.

Lifecycle methods

A component instance can provide following lifecycle methods.

Name Description

init(componentContext)

Signals the start of the injection phase of the component. It is the first method called by the App Runtime after the construction of the instance. Services are injected after this method is called, but the special properties _properties and _i18n are already usable.

activate(componentContext)

Signals the end of the injection phase and that the component instance is ready for use. The App Runtime guarantees that service consumers can access the component instance only after activate is called on the instance.

[NOTE] Only immediate components are allowed to return a dojo.Deferred instance in the activate method to delay the following processes until the deferred is resolved.

createInstance()

This method is called after activate if instanceFactory is set to true.

modified(componentContext)

Signals that the component properties(configuration) is changed. This method is only called between activate and deactivate. The property _properties already contains the new properties.

destroyInstance(instance)

This method is called directly before deactivate if "instanceFactory" is set to true. If destroyInstance is not provided by the component instance, the App Runtime checks for a destroy or destroyRecursive method on the instance for self-destruction. (destroyRecursive supersedes destroy)

deactivate(componentContext)

Signals the start of the shutdown process of the component instance. All injected services are still available and the ejection phase starts after this method.

destroy() or destroyRecursive()

Signals the end of the life of the component instance. All services are ejected before this method, but _properties and _i18n are still accessible. The method destroyRecursive supersedes the destroy method if available, this rule is introduced for better Dijit widget support.

These methods are called in the life of a component in the order as listed.

Activation

  1. constructor: properties are handed over (if "propertiesConstructor" is true)

  2. init: is called before references are injected, _properties and _i18n are available

  3. set<ReferenceName>, add<ReferenceName>: App Runtime injects services

  4. activate: is called after references are injected

  5. createInstance: called if "instanceFactory" is true and must return the service instance

Running

  • modified: called if configuration is modified; _properties contains the new configuration properties.

  • set<ReferenceName>, add<ReferenceName>: App Runtime injects new registered services, if "policy": "dynamic".

  • unset<ReferenceName>, remove<ReferenceName>: App Runtime ejects unregistered services, if "policy": "dynamic".

Deactivation

  1. destroyInstance: called if "instanceFactory" is true and should clean up the service instance

  2. deactivate: is called before references are ejected from the component

  3. unset<ReferenceName>, remove<ReferenceName>: App Runtime ejects services

  4. destroy or destroyRecursive: is called after references are removed, _properties and _i18n are still available

Service references

A very powerful feature of the component system is the declarative expression of references to other services registered in the App Runtime. The declarative component model significantly simplifies the handling of the dynamics of service dependencies.

A reference expression is a declarative wrapper of the method BundleContext.getServiceReferences (described in Programmatic service access), which requires an interface as selector together with an optional filter expression. Services which are selected by these conditions are called target services. The properties providing, filter and cardinality of a reference specify rules which are checked by the App Runtime. If all rules of a reference are fulfilled, the reference is said to be satisfied. A component configuration becomes satisfied when all references are satisfied. If a reference becomes unsatisfied, the component configuration also becomes unsatisfied and is deactivated. If a component configuration is activated by the App Runtime, some of the target services are bound to the configuration. These services are called bound services.

References are expressed as part of the component description.

manifest.json file: references of components
{
  "name" : "mybundle",
  ...
  "components" : [
  {
    "name" : "MyComponent",
    ...
    "references" : [
      {
        "name" : "logService",
        "providing" : "ct.framework.api.LogService"
      },
      ...
    ]
  },
  ...
  ]
}

A reference needs at least a name and a providing property. The name property must have a unique value inside the references of the component description and identifies the reference. The providing property defines the interface which the target service must provide.

Filtering of references

The property filter at a reference is used to further reduce the target services selected by the providing property. The value of filter is a filter expression described in the section Searching for bundles.

References filter property
{
  "name" : "myFactory",
  "providing" : "ct.framework.api.ComponentFactory",
  "filter" : "(Component-Factory=MyComponentFactory)"
}

It is allowed to use property placeholders in filter expressions, like in the following code sample.

References filter property
properties: {
   "storeId" : "sample-store"
},
references:[{
    "name": "store",
    "providing": "ct.api.Store",


    // the string '{storeId}' is replaced by the value of the property 'storeId' of the component configuration
  "filter": "(&(useIn=selection)(id={storeId}))"
}]

Placeholders in filter expressions allow the change of the target set of a reference based on configuration properties.

Reference cardinality

By specifying the property "cardinality" on a reference you can declare target set conditions.

References cardinality property
{
  "name" : "myFactory",
  "providing" : "myservice.Interface",
  // optional reference, only one target service needed
  "cardinality" : "0..1"
}

Following cardinality values are supported:

Expression Description

1..1

Selects exactly one target service from the set of target services and a target service must be available to become satisfied. This is the default if no property is defined.

0..1

The reference is satisfied if none or more target services are available. Selects one service from the set of target services, if available.

1..n

Selects all target services from the set of target services and a target service must be available to become satisfied.

0..n

The reference is satisfied if none or more target services are available. Selects all services from the set of target services, if available.

If only one target service is selected then the first service returned by BundleContext.getServiceReferences is bound, which is the one with the highest ranking or the one first registered.

Reference policy

A special lifecycle property is the "policy" property on a reference. It can have the value "dynamic" or "static" and declares the capability of a component instance to react on runtime changes of the matching target services of a reference. The policy is only important if changes in the target services still keep the reference satisfied. For example, this is the case if a reference has a 1..n cardinality and a new target service is registered at the system.

Value Description

dynamic

Defines that the component instance is able to react dynamically on changes in the bound service set itself. Therefore the App Runtime is not restarting the component configuration.

This is the default value.

static

Defines that the component instance is not able to react on dynamic changes in the bound service set. This means that the App Runtime restarts the component configuration on such changes.

References policy property
{
    "name" : "myFactory",
    "providing" : "myservice.Interface",
    "cardinality" : "0..n",
    // defines that the component cannot handle dynamic changes of that reference
    "policy" : "static"
}

Accessing references

The bound service set of declared references are provided by the App Runtime to a component instance in different ways. The App Runtime distinguishes between following main strategies:

  • Injection Strategy: References are transparently injected into the component instance and access is made via member variables in the component instance.

  • Event Strategy: References are injected into the component instance by method calls. The App Runtime calls specific methods provided by the component instance to inform about new services and service removal.

  • Lookup Strategy: References are manually looked up by the component instance in the component context.

All strategies can be mixed inside a component instance, very common is a mix of Injection Strategy and Event Strategy. The Lookup Strategy is rarely used.

All these strategies are discussed in the following sections on this sample component description:

Component description sample
{
    "name" : "mybundle",
    ...
    "components" : [
    {
        "name" : "MyComponent",
        "references" : [
            {
                "name" : "logService",
                "providing" : "ct.framework.api.LogService"
            },
            {
                "name" : "stores",
                "providing" : "ct.api.Store",
                "cardinality" : "0..n"
            }
        ]
    }
    ]
}

Injection strategy

The injection strategy means that references are provided as member properties inside the component instance without any further effort on the side of the component instance. The component instance can directly access the injected properties. The injected properties are named exactly like the name property of the reference. To access the service properties of an injected reference, the special property <name>_info is injected.

Reference injection
export default class {
        doSomething(){

            // simply access the logService service instance,
            // because of the default 1..1 cardinality it is guaranteed to be available
            this.logService.info("DoSomething started!");

            // read service properties
            let logserviceProperties = this.logService_info;

            // this.stores contains all stores in the system (0..n cardinality)
            // if no stores are in the system, the array is still present but empty
            let stores = this.stores;

            // read service properties of stores, is an array, too
            let storeProperties = this.stores_info;

            stores.forEach((store,index)=>{
                // get service properties of current store
                let storeProps = storeProperties[index];
                let ranking = storeProps["Service-Ranking"];
                ...
            });
        }
}
Injection/ejection happens before activate and after deactivate and of course in between if the policy is dynamic and the target service set changes. For details, see component lifecycle methods.

Event strategy

The event strategy is used by the App Runtime to inject services into the component instance if the instance provides event methods. The names of the event methods are calculated based on the multiplicity of the cardinality and the name of the reference.

Cardinality Name Calculation Pattern Sample

0..1 or 1..1

set<ReferenceName>

unset<ReferenceName>

setLogService : function(logservice, serviceproperties){ …​ }

unsetLogService : function(logservice, serviceproperties){ …​ }

0..n or 1..n

add<ReferenceName>

remove<ReferenceName>

addStores : function(store, serviceproperties){ …​ }

removeStores : function(store, serviceproperties){ …​ }

The name of the reference is capitalized to create the method name, so logService is converted into LogService.
Reference Event Strategy
export default class {
        constructor(){
            this.logger = null;
            this.stores = [];
        }

        // called by App Runtime to bind the log service
        setLogService(logservice, serviceproperties){
            this.logger = logservice;
        }

        // called by App Runtime to inform about unregistration of the log service
        unsetLogService(logservice, serviceproperties){
            this.logger = null;
        }

        // called by App Runtime to inform about a new store
        addStores(store, serviceproperties){
            this.stores.push(store);
        }

        // called by App Runtime to inform about unregistration of a store
        removeStores(store, serviceproperties){
            var index = this.stores.indexOf(store);
            this.stores.splice(index,1);
        }

        doSomething(){
            this.logger.info("DoSomething started!");
            this.stores.forEach((store)=>{
                ...
            });
        }
}

These default method names can be overwritten by defining the "bind" and "unbind" properties at the reference description.

Reference with custom bind
{
    "name" : "mybundle",
    ...,
    "components" : [
    {
        "name" : "MyComponent",
        "references" : [
            {
                "name" : "logService",
                "providing" : "ct.framework.api.LogService",

                // declare custom event methods (setLogging is used for bind and unbind)
                "bind" : "setLogging",
                "unbind" : "setLogging"
            }
        ]
    }
    ]
}

This results in an implementation like this:

Reference with custom bind method
export default class {

        constructor(){
            this.logger = null;
        }

        // called by App Runtime to bind and unbind the log service
        setLogging(logservice, serviceproperties){
            if (this.logger){
                // unset
                this.logger = null;
            }else{
                this.logger = logservice;
            }
        }

        doSomething(){
            this.logger.info("DoSomething started!");
        }
}
The event methods are called (like injection) before activate and after deactivate and of course also between if the policy is dynamic and the target service set changes. For details, see component lifecycle methods.

Lookup strategy

A third variant for retrieving the services is to use the ComponentContext. The ComponentContext is provided by the activate method (see component lifecycle methods).

Reference-relevant methods of the ComponentContext are:

Method Description

locateServiceReferences : function(referenceName)

Returns an array of service references. The matching target services.

locateServices : function(referenceName)

Returns an array of service instances. The bound service set.

locateService : function(referenceName, serviceReference)

Returns service instance selected by the service reference.

The usage style is similar to working with the BundleContext. Services, which are located by using locateService or locateServices, should not be stored in member variables. The 'locate*' methods should only be called between activate and deactivate. For details, see component lifecycle methods.

References via lookup
export default class {

        activate(componentContext){
            // store component context in member variable
            this.componentContext = componentContext;
        }

        deactivate(componentContext){
            // clear member variable
            this.componentContext = null;
        }

        doSomething(){
            let componentContext = this.componentContext;
            // cardinality is 1..1 so the result array has exactly 1 item, the log service
            let logger = componentContext.locateServices("LogService")[0];
            logger.info("DoSomething started!");

            // get all stores
            let stores = componentContext.locateServices("Stores");

            stores.forEach((store)=>{
                ...
            });
        }
}

If the lookup style is used in a component instance, the special property "noInjection" should be set on the reference description to ensure that the App Runtime does not inject the references into the component instance.

References: noInjection property
{
    "name" : "mybundle",
    ...,
    "components" : [
    {
        "name" : "MyComponent",
        "references" : [
            {
                "name" : "logService",
                "providing" : "ct.framework.api.LogService",
                // disable injection behavior
                "noInjection" : true
            },
            {
                "name" : "stores",
                "providing" : "ct.api.Store",
                "cardinality" : "0..n",
                // disable injection behavior
                "noInjection" : true
            }
        ]
    }
    ]
}

Declarative event binding to references

A further feature of the component system is the declarative event binding. This is of interest if a component instance likes to listen on events on a reference and to execute a special method if that event is fired.

It can be distinguished between three kinds of event binding styles: "connect", "on", and "watch".

Style: on

This style uses the dojo/on module to connect to events provided by services. The "on" property on a reference declares the binding.

References policy property
{
    "name" : "aTool",
    "providing" : "ct.tools.Tool",
    "filter" : "(id=atoolname)",

    // on is an object with event names to event handle methods
    "on" : {
        // Click = event name fired by tool
        // _handleOnClick = the method which should be called if the event fires
        "Click" : "_handleOnClick"
    }
}

The code of the component instance might look like this:

MyComponent implementation using injected properties
export default class {

    _handleOnClick(event){
        // do something...
        console.debug("clicked..", event);
    }
}

Style: connect

This style should be replaced by using the on style, when possible.

This style uses the dojo/_base/connect module to connect to events provided by services. The "connect" property on a reference declares the binding.

References policy property
{
    "name" : "aTool",
    "providing" : "ct.tools.Tool",
    "filter" : "(id=atoolname)",

    // connect is an object with event names to event handle methods
    "connect" : {
        // onClick = event fired by tool
        // _handleOnClick = the method which should be called if the event fires
        "onClick" : "_handleOnClick"
    }
}

The code of the component instance might look like this:

MyComponent implementation using injected properties
export default class {

    _handleOnClick(event){
        // do something...
        console.debug("clicked..", event);
    }
}

Style: watch

This style declares a property change listener. It can be used at services which provides watch methods, like defined by dojo/Stateful. All ct/Stateful and dijit/_Widget instances provide such a method.

The "watch" property on a reference declares the binding.

References policy property
{
    "name" : "aStateful",
    "providing" : "a.datamodel.Instance",

    // watch is an object with property names to property change listener methods
    "watch" : {
        // selectedItem = name of the property about whose value changes should be informed
        // _handleSelectedItemChange = the property change listener method
        "selectedItem" : "_handleSelectedItemChange"
    }
}

The code of the component instance might look like this:

MyComponent implementation using injected properties
export default class {

    _handleSelectedItemChange(propertyname, oldvalue, newvalue){
        // do something...
    }
}