Follow

Creating Your Own Control

There may be a situation where the available controls in the SPARK UI Toolkit do not fit your needs.

The below example will walk you through how to create your own control using SPARK.

At the bottom of this article you will find two JavaScript files.  The ControlTemplate.js file will help you in creating your own controls, and the MyButton.js file is what was used in this example.

This example is to help developers gain a better understanding of the SPARK UI Toolkit and is by no means exhaustive.

Creating a Control Example

This example will show you how to create a button control.  Although there is already a button control in the SPARK UI Toolkit, this guide demonstrates how you would create your own control with SPARK.

Before you begin, make sure the SPARK UI Toolkit is added to your Process App.

See Installing SPARK V4 In IBM BPM V8.5.X for more information

Once you have the SPARK UI Toolkit in your Process App, create a coach view. Click the plus (+) button next to User Interface in Process Designer and select Coach View.

Name your coach view.

To create your own view, you need to add a couple of things.  Click the plus (+) button next to Included Scripts in the coach view's behavior tab.

  • BPMExt-Core.js

 

The BPMExt-core.js file is part of the BPMExt-core.zip file.  The BPMExt-core.zip file contains the BPMExt-core.js loader, which loads what's inside the BPMExt-core.zip file.

Now, you need to update your AMD dependency to point to the bpmext.js file inside of the BPMExt-core.zip file.

Asynchronous Module Definition (AMD) dependencies are used to load libraries into coach views.  More information about AMD in Dojo can be found at the following website: https://dojotoolkit.org/documentation/tutorials/1.10/modules/

Under AMD Dependencies, add the following:

Click image for larger view

The Module ID is com.salientprocess.bpm.views/bpmext and the Alias is bpmext.

What this does is sets bpmext (our alias) equal to com.salientprocess.bpm.view.bpmext which points to the bpmext.js file within the BPMExt-core.zip file.

bpmext.ui.loadView (registers the coach view instance as a SPARK control) and bpmext.ui.unloadView (unregisters the coach view instance from SPARK) comes from the bpmext.js file.  You can see more about the namespaces under bpmext here: https://support.salientprocess.com/docs/enterprise/bpmext.html

Click image for larger view

Now, create a new JavaScript file.  The template used is attached at the bottom of the article.

Change MyControl in bpmext_control_InitMyControl = function (utilities, bpmext) to the name of your coach view, for our example, we would change it to: bpmext_control_InitMyButton = function (utilities, bpmext).

All we want is bpmext, so remove the utilities parameter.  When done, it will look like this: bpmext_control_InitMyButton = function (bpmext)

Add bpmext_control_InitMyButton.call(this, bpmext) in the Inline JavaScript of your coach view to be able to call externally defined functions.

Click image for larger view

I want to call MyButton as a function as if it belongs to the coach view itself. That's why we use call, and have the first parameter of this.  In this context, this points to the instance of the MyButton coach view.  Then we pass in the AMD dependency bpmext.

At this point, make sure you've saved your JavaScript file.  For this example, we've saved it as MyButton.js.

Next we want to register MyButton, so that it works with addressing and other functions provided by the SPARK UI Toolkit.

So, we want to make sure in load, under this.constructor.prototype.load = function () in our MyButtion.js file has the following:

bpmext.ui.loadView(this);

this points to the coach view instance.

Note that the prototype.load(), prototype.view(), prototype.change(), prototype.validate(), prototype.collaboration(), and prototype.unload() represent Coach NG's Event Handlers of the same name.

We also want to add:

bpmext.ui.unloadView(this); to unload under: this.constructor.prototype.unload = function ()

Now, add your MyButton.js file to Files in your Process App, so we can test what we've done so far by creating a coach and dropping your MyButton coach view onto it.  You can drag and drop your file into Files, or browse for your MyButton.js file, use the plus (+) button next to Files to add your MyButton.js file.

 

Then add the MyButton.js file to the Included Scripts in the MyButton coach view.

Instead of using Layout in the MyButton coach view, we will be creating a button control in the MyButton.js file.  We can do it before or after load view.  However, if there are errors before load view, we don't want to actually load the view, so we will delay load view towards the end.

this.constructor.prototype.load = function ()
{
     try
     {
          var btn = document.createElement("button");
          btn.innerHTML = this.context.options._metadata.label.get("value"); //This allows us to use the Label section to name our button control

          this.context.element.appendChild(btn);

          bpmext.ui.loadView(this);
     }

Now, we will create a simple config property, like a color for the font.

In the MyButton coach view, under Variables, add a configuration option for the text color.

Click the plus (+) button next to Configuration Options, name it color, and put the Label as Text Color.

Now that we've added the color config option, we need to add the color variable to our MyButton.js file.

Under instance at the beginning of the MyButton.js file we add:

_setColor : function(view)
{
     var color = view.context.options.color.get("value");

     if(color != null && color!= "")
     
          view._instance._btn.style.color = color;
     else
          view._instance._btn.style.color = null;

}

If there is no color specified at load time, the button will have a default text color of black.  As set under the load function under the Coach NG Lifecycle methods in the MyButton.js file.

if(!opts.color)
bpmext.ui.substituteConfigOption(this, "color", "black");

The substituteConfigOption function is defined in the bpmext.js file and takes in the following parameters: view, propName, defVal.  Use of this method allows you to treat a config option as always existing, since this method will create the config option if it does not exist.

If we want to change the text color after the page loads, we need to add an on change handler.

In order to do this, there needs to be a reference to the button.  This is done by storing it with the instance data, which is under: this._instance at the top of the MyButton.js file.  

Now all we have to do is add this._instance._btn = btn; under this.context.element.appendChild(btn) under the load function under the Coach NG Lifecycle methods in the MyButton.js file, which saved the button that was created at load time as an instance variable.

In order to make the developer's life easier, we can create a setColor method.

Under the Public control methods section of your MyButton.js file, add the following:

this.constructor.prototype.setColor = function (color)
{
this.context.options.color.set("value", color);
}

The setColor fires the change event.

Now, all a developer has to do to call this method in the console is:

Click image for larger view

Note: Make sure your most recent MyButton.js file is in your process app, and run your test coach.

We can also add a getColor method under the Public control methods in the MyButton.js file:

this.constructor.prototype.getColor = function ()
{
return this.context.options.color.get("value");
}

Creating an action on the on click event of our button can be done by registering the on click event before you call load.

Create another configuration option in your MyButton coach view called onclickEvent.

Now add the on click event to your MyButton.js file.  Under the Coach NG Lifecycle methods in your MyButton.js file, put the blue text below in the load function:

/*
Coach NG Lifecycle methods *************************************************************
*/
this.constructor.prototype.load = function ()
{
     try
     {
          bpmext.ui.registerEventHandlingFunction(this, "onclickEvent");

          var btn = document.createElement("button");
          btn.innerHTML = this.context.options._metadata.label.get("value");

          var opts = this.context.options;

          if(!opts.color)
          bpmext.ui.substituteConfigOption(this, "color", "black");

          this.context.element.appendChild(btn);
          this._instance._btn = btn;

          this._proto._setColor(this);

          var view = this;
          btn.onclick = function()
          {
               bpmext.ui.executeEventHandlingFunction(view, "onclickEvent");
          }

          bpmext.ui.loadView(this);
    }
    catch (e)
    {
          bpmext.log.error("Error on load event [" + this.context.viewid + "]: " + e);
          if(e.stack)
               bpmext.log.error(" Call stack: " + e.stack);
    }
};

when I click on my button I'd like it to fire the above function, since we used the registerEventHandlingFunction it's going to interpret whatever is put in the onclickEvent config option as code.

However, the button has no way if knowing that is has been clicked, so we add the code in blue below to the MyButton.js file under the load function under the Coach NG Lifecyle methods.

/*
Coach NG Lifecycle methods *************************************************************
*/
this.constructor.prototype.load = function ()
{
     try
     {
          
bpmext.ui.registerEventHandlingFunction(this, "onclickEvent");

          var btn = document.createElement("button");
          btn.innerHTML = this.context.options._metadata.label.get("value");

          var opts = this.context.options;

          if(!opts.color)
          bpmext.ui.substituteConfigOption(this, "color", "black");

          this.context.element.appendChild(btn);
          this._instance._btn = btn;

          this._proto._setColor(this);

          var view = this;
          btn.onclick = function()
          {
               bpmext.ui.executeEventHandlingFunction(view, "onclickEvent");
          }

          bpmext.ui.loadView(this);
    }
    catch (e)
    {
          bpmext.log.error("Error on load event [" + this.context.viewid + "]: " + e);
          if(e.stack)
               bpmext.log.error(" Call stack: " + e.stack);
    }
};

registerEventHandlingFunction and executeEventHandlingFunction are defined in the bpmext.js file.

Make sure you have your most up to date MyButton.js file in your process app by either dragging and dropping your file into Files, or browse for your MyButton.js file.  This will override your old file.

So now, we can put the following in our test coach:

and at runtime:

 

We get free methods from our framework, like addClass, setVisiblie, and setEnabled.  However, the framework doesn’t inherently know how to disable a control, so, we have to add in that functionality to our MyButton.js file.

Create a handle visibility function in a central location at the top of your MyButton.js file:

bpmext_control_InitMyButton = function (bpmext)
{
     this._instance = {
     };

     if (!this.constructor.prototype._proto)
     {
          this.constructor.prototype._proto =
          {
               EVT_ONCLICK : "eventON_CLICK",
               _setColor : function(view)
               {
                    var color = view.context.options.color.get("value");

                    if(color != null && color!= "")

                         view._instance._btn.style.color = color;
                    else
                         view._instance._btn.style.color = null;
               },
               _handleVisibility : function(view)
               {
                         var vis = view.context.options._metadata.visibility.get("value");
                         view._instance._btn.disabled = (vis == "READONLY");

               }
};

Now, reference this function in the change handler:

this.constructor.prototype.change = function (event)
{
     try
     {
           if (event.type == "config")
          {
               switch (event.property)
               {
                    case "_metadata.visibility":
                    {
                         this._proto._handleVisibility(this);

                         break;
                    }
                    case "color":
                    {
                         this._proto._setColor(this);
                         break;
                    }
               }
          }
     }
     catch (e)
     {
          bpmext.log.error("Error on change event [" + this.ui.getAbsoluteName() +           "]: " + e);
          if(e.stack)
               bpmext.log.error(" Call stack: " + e.stack);
     }
};

Now we can disable our button with the setEnabled method.

I hope you've found this tutorial helpful, and are able to see how SPARK improves both the authoring experience and the end-user experience for IBM BPM Coach NG User Interface Services. Improved experience means a streamlined and more agile experience for a BPM UI author. For a customer team it means a more iterative experience and a faster way to implement business requirements. For an end-user it means a richer interaction with BPM Coach Pages and visual elements that behave with good performance characteristics. SPARK is completely based on the IBM BPM Coach NG framework. It builds on Coach NG by providing a large set of new and/or improved controls (90+ compared to 15+ Coach NG stock controls).

Notes on Creating Container Controls

When creating a container controls the key difference is that you use bpmext.ui.loadContainer(this) and bpmext.ui.unloadContainer(this) in the load and unload event handlers.

There is however one more important item that you must take care of.  As you likely know, the addressing of controls inside container controls does not contain the containers Control Id as part of the address.  For the known containers this is handled internally, but if you are creating a container you must handle this yourself.  This is accomplished by putting the following code in the Inline Javascript section of the Behavior tab for your new container control:

this.IS_ADDRESS_INVISIBLE = true;

  • Author: Courtney Silva
  • Date Created: March 30, 2016
  • Date Modified: November 10, 2016
Was this article helpful?
1 out of 1 found this helpful
Have more questions? Submit a request

Comments