Imagine you want to see all IBM BPM coach validation errors in one place. By default after validation phase you need to find all invalidated widgets one by one, changing tab or opening accordions, and so on. Coach framework does not give any programmatic tool to get all validation messages from inside of coach view. While SPARK / BPMUI library gives some helper functions it does not cover all cases. Here I discuss simple way to plug-in to (or hack) coach engine and SPARK library to collect all validations at once.
The idea of problem is depicted below. There is a coach/screen that does validation, like on diagram aside, marking variables as invalid. On way back to coach the framework engine processes them and propagates invalid states to widgets. This way each coach view can be notified, via validation even handler, that its validity has changed – see (1) and (2) markers on screenshot below. This is the only extension place provided by coach framework and it is not enough to build single coach view that would display all errors at once, as I imagined (and implemented) like part (3) marked below. In some use cases there are errors not connected to any widget (like failing server calls) or validations that are coupled in a way that viewing them in one place would give user chance to solve them faster. The SPARK UI toolkit (that became official UI library in BPM 8.6) put on top of coach framework comes with great help in validations. It introduces its own validation mechanics and distribution as SPARK widgets may work without bound variables yet being marked as valid or invalid via setValid() method on widgets. This way any invalidated SPARK widget, bound or boundless, can be reported by global method bpmext.ui.getInvalidViews(). This also works for custom widgets that are being initialized as SPARK compatible ones, via bpmext.ui.loadView() or bpmext.ui.loadContainer() methods during widget load phase. Minor issue is that SPARK approach does not cover overusing the tw.system.coachValidation with errors attached to non-existent variables one could use for widgetless errors. More limiting is lack of global validation notification on SPARK level, so that client is aware when validation state of screen changes and could re-read bpmext.ui.getInvalidViews() to get new results.To solve first problem I took a look on how coach engine does the the validation. I placed breakpoint inside validate handler of my custom coachview to stop debugger there and inspect stack of calls. It immediatelly led me to handleValidationEvent inside the com.ibm.bpm.coach/engine module. While engine code is minified its public method names are still giving clue on processing steps and their sequence of calls. This way I realized that each view visited on validation gets called _deliverValidationEvents(event, viewMap, isClear) where event contains context information of processed validation, its event.domId is a key in viewMap? giving coach view object being validated, and optional flag isClear is set to true when validation is being cleared on widget instead of set new error. In addition I proxified handleValidationEvent to know when all validations erros are collected.
To cover all validations, including boundless SPARK widgets, I needed extra step. Review of bpmext package pointed me to updateViewValidationState method that is declared to be used by all widgets during validation phase. So I had candidate to be notified when SPARK is being manipulated.
require(["com.ibm.bpm.coach/engine"], function (engine) { var dve = engine._deliverValidationEvents; engine._deliverValidationEvents = function (event, viewMap, isClear) { dve(event, viewMap, isClear); // original processing first console.log("_deliverValidationEvents", event, viewMap, isClear); }.bind(engine); var hve = engine.handleValidationEvent; engine.handleValidationEvent = function (event) { hve(event); console.log("handleValidationEvent", event); }.bind(engine); }); var uvvs = bpmext && bpmext.ui && bpmext.ui.updateViewValidationState; if (uvvs) { bpmext.ui.updateViewValidationState = function (view, event) { uvvs(view, event); //call original handler console.log("updateViewValidationState", view, event); }; } |
Final result is that I have build two coaches. One to collect validations and another one to display them. When first one finishes its work using proxies described above, it fills in own binding being list of validation errors from all widgets. Second widget bound to that list listens on binding change notification and redisplays list of errors immediatelly after revalidation changes it. Such decoupling makes easier to adopt different display strategies per project without rebuilding or customizing widget collecting data.
Wow. This is such a great work. I’ve been stuck with the same problem for a view days now. Can you provide a .twx for this implementation ?
This is my email address : mgamal19977@gmail.com