IBM BPM Software

Intercepting Boundary Events in Coach View

Boundary Event is core concept of UI know-how in IBM BPM: any visual component (coach view) of display (coach) can hearth such occasion shifting management from current display to some other place, crossing “display boundary“. In truth Boundary Event is the one option to make such transition on diagram of coaches. Drawback is that IBM BPM occasion model is trivial and doesn’t permit nearly any interference with occasion in progress, one can’t change event effervescent or deal with it in try-catch manner. Find how I bypassed this limitation and carried out events interceptor.

Widespread processing

Separate processing

Separate processing

Imagine display that has a number of buttons. They’re typical source of boundary occasions and if wired on diagram they could lead to applicable processing – see diagram aside for three buttons with 3 distinct scripts to run on each button click on.
What if some buttons are serve comparable function, like totally different variants of motion to take. In such condition their boundary event might lead to the identical script. Now the problem is to know which button was clicked. In stock coaches one might bind every button to boolean variable and verify them one after the other: shortcoming is human service is polluted with unnecessary variables (which is an issue of IBM BPM coaches anyway). With SPARK UI toolkit one might add easily on-click event handler storing, let say, button view ID in shared variable.

I imagined wrapper view that intercepts button boundary occasions and triggers own one as an alternative. Wrapper would expose binding variable with source of occasion (view ID) to make answer full. I created sample state of affairs (see under) that has coach view container internet hosting other controls and intercepting their boundary events.

Test scenario - diagram

Wanting closer at coach framework JavaScript API, it’s straightforward to spot that there are solely two features coping with boundary events: context.trigger(callback) and context.cancelBoundaryEvents(). Notice nevertheless the latter works solely from inside callback of trigger perform. However both features work on the degree of event supply (widget triggering event) there isn’t any a lot room for play with occasion in progress.

Browser JavaScript nevertheless provides a approach for some trick to bypass source limitations: in my case I noticed that I might… substitute context.set off() on all views I need to take management over! First step was to make youngsters traversal. It was structured into recursive walk and delegation of remaining work to visitor perform (all code in Inline JavaScript section).

this._visitChildren = perform(view)
var rootView = this;
if (view && view.context && view.context.subview)
if (rootView !== view)
rootView._visitor(view);

var viewIds = Object.keys(view.context.subview);
viewIds.forEach( perform(id)
var subviews = view.context.getSubview(id);
subviews.forEach( perform(sv)
rootView._visitChildren(sv);
);
);

;

this._visitChildren = perform(view)
var rootView = this;
if (view && view.context && view.context.subview)
if (rootView !== view)
rootView._visitor(view);

var viewIds = Object.keys(view.context.subview);
viewIds.forEach( perform(id)
var subviews = view.context.getSubview(id);
subviews.forEach( perform(sv)
rootView._visitChildren(sv);
);
);

;

Customer takes every subview and replaces trigger perform with proxy. Proxy saves event source (view ID) in variable sure on root interceptor degree and then calls interceptor’s set off():

this._visitor = perform(subview)
console.log(“Proxying ” + subview.context.viewid);
var rootView = this;
var set off = subview.context.set off;
subview.context.trigger = perform(callback, options)
console.log(“trigger() proxy called for ” + subview.context.viewid);
//trigger.call(subview, callback, options); //unique trigger name
rootView.context.binding.set(“value”, subview.context.viewid);
rootView.context.trigger(callback, choices);
;

this._visitor = perform(subview)
console.log(“Proxying ” + subview.context.viewid);
var rootView = this;
var trigger = subview.context.set off;
subview.context.trigger = perform(callback, choices)
console.log(“trigger() proxy called for ” + subview.context.viewid);
//trigger.name(subview, callback, choices); //unique trigger call
rootView.context.binding.set(“value”, subview.context.viewid);
rootView.context.set off(callback, choices);
;

That was fairly straightforward till I realised that it’ll not work for views that mutate in runtime. That is what happens when editable desk is used e.g. adding row means creating views after preliminary load. Or when panel is lazily loaded like in custom management that initialise panels after they develop into visible e.g. on tabbed section.

Thankfully proxying is sort of powerful concept. As a result of creating views in runtime is completed by calling to context.createView() run on present view, it signifies that any additional view inside tree of youngsters of interceptor have to be referred to as from already-proxied view. Meaning additional duty of createView() was to re-visiting nodes after creation:

this._visitor = perform(subview)

var createView = subview.context.createView;
subview.context.createView = perform(domNode, index, parentView)
console.log(“createView() proxy called for ” + subview.context.viewid);
createView.name(subview.context, domNode, index, parentView);
// createView on subview might recursively create multiple views
rootView._visitChildren(subview);
;

this._visitor = perform(subview)

var createView = subview.context.createView;
subview.context.createView = perform(domNode, index, parentView)
console.log(“createView() proxy called for ” + subview.context.viewid);
createView.call(subview.context, domNode, index, parentView);
// createView on subview might recursively create a number of views
rootView._visitChildren(subview);
;

Since views might be created on any degree of youngsters it means similar sub-trees may be probably visited repeatedly. Above code without checks merely leads to layering extreme proxies (proxy on proxy and so forth). Adding flag on each subview and guard situation on visitor is straightforward and efficient counter-measure. Tuned customer perform seems to be like this:

this._visitor = perform(subview)
if (subview._bec_proxified)
return;

subview._bec_proxified = true;

this._visitor = perform(subview)
if (subview._bec_proxified)
return;

subview._bec_proxified = true;

To show every thing works as expected I created display with few buttons on totally different ranges of nesting. To simulate dynamic views creation I made up coach view that manages content-box itself and masses baby view after a while delay. Boundary Occasion Collector and Delayed load coach views has visible guides: vibrant border frames, green and pink respectively.

Console in browser’s inspector (display under) exhibits how youngsters are visited throughout on-load part. B1 and B2 as prime degree, then section with B3. After 5 seconds delay, Delayed load coach view is loaded and notice it happens from createView() proxy, so that when unique context.createView() finishes loading CV and button B4, visiting continues again. Lastly after clicking on B4 proxified set off known as and second display shows source of registered boundary event.

Delayed load coach view created for testing interceptor touches certainly one of fascinating advanced topics in coach know-how: dynamic manipulation on youngster views lifecycle i.e. creating them or deleting in runtime. This function is never used in real life situations on software degree improvement, but it’s good to know fundamentals.

By default content-box is managed by framework: views placed in content-box are initialised mechanically earlier than mother or father’s load occasion handler known as; it signifies that throughout on load part youngsters are full blown coachviews already.
be-cboxWhen content-box is configured to self-manage its content, it signifies that dad or mum (our) coach view should do the job utilizing context.createView() on gadgets placed throughout design part (and for runtime views as properly). Because baby views might be nested forming a tree construction, it is our CV duty to walk over it. In my case delayed creation appeared like this:

this._loadChildren = perform(view)
var cb = view.context.aspect.querySelector(“.ContentBox”);
for(var i = zero; i < cb.youngsters.length; i++) var youngster = cb.youngsters[i]; if(domClass.incorporates(baby, "ContentBox")) this._loadChildren(baby); else if(domAttr.has(youngster, "data-viewid")) attempt this.context.createView(youngster, null, this); catch(e) console.error(e);

this._loadChildren = perform(view)
var cb = view.context.component.querySelector(“.ContentBox”);
for(var i = zero; i < cb.youngsters.size; i++) var youngster = cb.youngsters[i]; if(domClass.incorporates(youngster, "ContentBox")) this._loadChildren(baby); else if(domAttr.has(baby, "data-viewid")) attempt this.context.createView(youngster, null, this); catch(e) console.error(e);

where domClass and domAttr are AMD dependencies on dojo/dom-class and dojo/dom-attr modules respectively.

For any additional learning, look into source code of present coach-based toolkits.

If you wish to play yourself you possibly can create the “Boundary Event Collector CV” yourself as proven under. You may also download entire venture with check suite in TWX type from section under.

  1. Create “Boundary Event Collector CV” coach view.
  2. In “Overview” section of this CV mark it “Can fire boundary event”.
  3. In “Behavior” part open “Inline JavaScript” section and paste
    content material of this JavaScript file.
  4. In “load” occasion handler of this CV call on-load perform like this:
    this.onLoad();
  5. In “Variables” part create String variable named controlID.
  6. In “Layout” section place “Content-box” widget, no particular setting is required.
  7. Finished!

Boundary_Event_Collector.twx (6.5MB) export file for BPM eight.5.7 model.