<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.infinite-erp.co.id/index.php?action=history&amp;feed=atom&amp;title=How_to_create_client_event_handler_actions</id>
	<title>How to create client event handler actions - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.infinite-erp.co.id/index.php?action=history&amp;feed=atom&amp;title=How_to_create_client_event_handler_actions"/>
	<link rel="alternate" type="text/html" href="https://wiki.infinite-erp.co.id/index.php?title=How_to_create_client_event_handler_actions&amp;action=history"/>
	<updated>2026-04-06T16:45:46Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.31.1</generator>
	<entry>
		<id>https://wiki.infinite-erp.co.id/index.php?title=How_to_create_client_event_handler_actions&amp;diff=70&amp;oldid=prev</id>
		<title>Wikiadmin: Created page with &quot;== Introduction == This howto discusses how to implement client side (javascript) functions which are executed before or after an event fired on a standard window of the [http...&quot;</title>
		<link rel="alternate" type="text/html" href="https://wiki.infinite-erp.co.id/index.php?title=How_to_create_client_event_handler_actions&amp;diff=70&amp;oldid=prev"/>
		<updated>2018-10-14T14:01:57Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;== Introduction == This howto discusses how to implement client side (javascript) functions which are executed before or after an event fired on a standard window of the [http...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;== Introduction ==&lt;br /&gt;
This howto discusses how to implement client side (javascript) functions which are executed before or after an event fired on a standard window of the [http://wiki.openbravo.com/wiki/User_Interface_Introduction User Interface].&lt;br /&gt;
&lt;br /&gt;
{{AvailableFrom|3.0PR16Q4}}&lt;br /&gt;
&lt;br /&gt;
== Example Module ==&lt;br /&gt;
&lt;br /&gt;
This howto is supported by an example module which shows examples of the code shown and discussed. &lt;br /&gt;
&lt;br /&gt;
The code of the example module can be downloaded from this mercurial repository: https://code.openbravo.com/erp/mods/org.openbravo.platform.ci/&lt;br /&gt;
&lt;br /&gt;
== Defining Client Event Handler Actions ==&lt;br /&gt;
&lt;br /&gt;
A client event handler action is a function in javascript available through a global ID. '''The global ID should be unique, it is strongly adviced to use the module's db prefix.''' The action has to be defined in a javascript file located in the module, see [[Client_Side_Development_and_API#Adding_javascript_to_Openbravo|here]] for information on howto add javascript code to Openbravo.&lt;br /&gt;
&lt;br /&gt;
The following is an example of this kind of actions: it shows a message '''after''' saving a record. The message type and content depends on if we are creating or updating the record.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
OB.OBPFCI = {};&lt;br /&gt;
OB.OBPFCI.ClientSideEventHandlers = {};&lt;br /&gt;
OB.OBPFCI.PRODUCT_CATEGORY_HEADER_TAB = '189';&lt;br /&gt;
&lt;br /&gt;
OB.OBPFCI.ClientSideEventHandlers.showMessage = function (view, form, grid, extraParameters, actions) {&lt;br /&gt;
  var data = extraParameters.data;&lt;br /&gt;
&lt;br /&gt;
  view.messageBar.keepOnAutomaticRefresh = true;&lt;br /&gt;
  if (extraParameters.isNewRecord) {&lt;br /&gt;
    // Save flow&lt;br /&gt;
    view.messageBar.setMessage(isc.OBMessageBar.TYPE_SUCCESS, 'New Record', 'Created Product Category with name ' + data.name);&lt;br /&gt;
  } else {&lt;br /&gt;
    // Update flow&lt;br /&gt;
    view.messageBar.setMessage(isc.OBMessageBar.TYPE_INFO, 'Updated Record', 'Updated Product Category with name ' + data.name);&lt;br /&gt;
  }&lt;br /&gt;
  OB.EventHandlerRegistry.callbackExecutor(view, form, grid, extraParameters, actions);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
OB.EventHandlerRegistry.register(OB.OBPFCI.PRODUCT_CATEGORY_HEADER_TAB, OB.EventHandlerRegistry.POSTSAVE, OB.OBPFCI.ClientSideEventHandlers.showMessage, 'OBPFCI_ShowMessage');&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As you can see the action is placed in a global object, in this case the module's ''dbprefix'' is used for that. It is important to note that you should not use ''var'' before the global object definition, otherwise your var is not global. This is because the global javascript code included in Openbravo is in fact executed within a function.&lt;br /&gt;
&lt;br /&gt;
This kind of actions receive 5 arguments:&lt;br /&gt;
&lt;br /&gt;
* '''view''': the standard view ([[Client_Side_Development_and_API#OBStandardView|OBStandardView]]) which provides access to the complete window and tab structure in a loaded window.&lt;br /&gt;
* '''form''': the [[Client_Side_Development_and_API#OBViewForm|OBViewForm]] which contains the fields, the form can also be the form used in inline grid editing.&lt;br /&gt;
* '''grid''': the [[Client_Side_Development_and_API#OBViewGrid|OBViewGrid]] which contains the list of records loaded for the tab. &lt;br /&gt;
* '''extraParameters''': extra information provided by the event handler. Its content depends on the type of event.&lt;br /&gt;
* '''actions''': the stack of actions to be executed. It must '''not be modified'''. It must be used just for invoking ''OB.EventHandlerRegistry.callbackExecutor'' as shown in the code above. This ensures the correct execution of all actions. &lt;br /&gt;
&lt;br /&gt;
{{(!)| Each action is responsible of invoking '''OB.EventHandlerRegistry.callbackExecutor'''. If an action does not call it, the subsequent actions (if any) will not be executed.}}&lt;br /&gt;
&lt;br /&gt;
== Registering, setting an Action for an Event within a Tab ==&lt;br /&gt;
&lt;br /&gt;
A client event handler action is linked to an event launched in a particular tab. This link is created by registering the action programmatically. Thus, it is possible to:&lt;br /&gt;
&lt;br /&gt;
* Add more than one action for a particular event in a tab&lt;br /&gt;
* Override/overwrite actions defined by other modules&lt;br /&gt;
&lt;br /&gt;
A client event handler action is registered through the '''OB.EventHandlerRegistry.register''' method. It expects 4 parameters:&lt;br /&gt;
* '''tab id'''&lt;br /&gt;
* '''event type''': the type of event that will cause the execution of the action &lt;br /&gt;
* '''callback function''': the client event handler action itself&lt;br /&gt;
* '''action id''': can be used to overwrite an existing action registered using the same id&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;OB.EventHandlerRegistry.register(OB.OBPFCI.PRODUCT_CATEGORY_HEADER_TAB, OB.EventHandlerRegistry.POSTSAVE, OB.OBPFCI.ClientSideEventHandlers.showMessage, 'OBPFCI_ShowMessage');&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Some comments about this code:&lt;br /&gt;
* ''OB.OBPFCI.PRODUCT_CATEGORY_HEADER_TAB'' is a constant we have defined which holds the id of the [http://wiki.openbravo.com/wiki/Product_Category Product Category] header tab.&lt;br /&gt;
* ''OB.EventHandlerRegistry.POSTSAVE'' is the event type.&lt;br /&gt;
&lt;br /&gt;
The '''event types''' currently supported are:&lt;br /&gt;
&lt;br /&gt;
* '''OB.EventHandlerRegistry.PRESAVE''': the action will be launched before creating or updating a record in a tab of a standard window. &lt;br /&gt;
* '''OB.EventHandlerRegistry.POSTSAVE''': the action will be launched after creating or updating a record in a tab of a standard window.&lt;br /&gt;
* '''OB.EventHandlerRegistry.PREDELETE''': the action will be launched before deleting a record in a tab of a standard window. This event type is available from version '''3.0PR17Q1'''.&lt;br /&gt;
&lt;br /&gt;
In the case of PRESAVE and POSTSAVE the '''extraParameters''' argument will contain the following information:&lt;br /&gt;
&lt;br /&gt;
* '''data''': the values of the record (before saving it in the case of '''PRESAVE''' and after saving it in the case of '''POSTSAVE''')&lt;br /&gt;
* '''isNewRecord''': a flag that indicates if the record is new. It can be use to distinguish between the '''save''' and '''update''' events.&lt;br /&gt;
&lt;br /&gt;
In the case of PREDELETE the '''extraParameters''' argument will contain the following information:&lt;br /&gt;
&lt;br /&gt;
* '''recordsToDelete''': the selected records in the grid which are going to be deleted, with the values of each record.&lt;br /&gt;
&lt;br /&gt;
==== Multiple Actions Functions per Event, Call Order ====&lt;br /&gt;
&lt;br /&gt;
The client event handler actions can have a sort property to control the call-order if there are multiple actions for one event in the same tab.&lt;br /&gt;
&lt;br /&gt;
It is for example set like this:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;OB.OBPFCI.ClientSideEventHandlers.showMessage.sort = 20;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Actions with a lower sort value will be executed before actions with a higher one. If an action does not have a sort defined it gets the sort 100 by default.&lt;br /&gt;
&lt;br /&gt;
==== Overriding/Replacing an Action ====&lt;br /&gt;
&lt;br /&gt;
An action can be registered using an id ('''action id'''). If there is already an action registered with the same id for the same tab and event type then it is replaced by the new registration.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
===  Post-save Action: Open a Tab ===&lt;br /&gt;
&lt;br /&gt;
The following example shows how to open a new tab '''after''' saving a record.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
OB.OBPFCI.PRODUCT_HEADER_TAB = '180';&lt;br /&gt;
OB.OBPFCI.ClientSideEventHandlers.openTab = function (view, form, grid, extraParameters, actions) {&lt;br /&gt;
  if (extraParameters.isNewRecord) {&lt;br /&gt;
    // Save flow&lt;br /&gt;
    OB.Utilities.openDirectTab(OB.OBPFCI.PRODUCT_HEADER_TAB);&lt;br /&gt;
  }&lt;br /&gt;
  OB.EventHandlerRegistry.callbackExecutor(view, form, grid, extraParameters, actions);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
OB.OBPFCI.ClientSideEventHandlers.openTab.sort = 120;&lt;br /&gt;
OB.EventHandlerRegistry.register(OB.OBPFCI.PRODUCT_CATEGORY_HEADER_TAB, OB.EventHandlerRegistry.POSTSAVE, OB.OBPFCI.ClientSideEventHandlers.openTab, 'OBPFCI_OpenTab');&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case we are opening the [http://wiki.openbravo.com/wiki/Product Product] window after creating a new [http://wiki.openbravo.com/wiki/Product_Category Product Category]. We use ''extraParameters.isNewRecord'' to identify the save flow. Finally we are invoking ''OB.EventHandlerRegistry.callbackExecutor'' to ensure the execution of the subsequent actions. In addition, we are giving a sort number of 120.&lt;br /&gt;
&lt;br /&gt;
=== Post-save Action: Refresh the Grid ===&lt;br /&gt;
&lt;br /&gt;
The following example shows how to refresh the grid '''after''' saving or updating a record.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
OB.OBPFCI.COUNTRY_HEADER_TAB = '135';&lt;br /&gt;
OB.OBPFCI.ClientSideEventHandlers.refreshGrid = function (view, form, grid, extraParameters, actions) {&lt;br /&gt;
  var viewInGridMode = !view.isShowingForm,&lt;br /&gt;
      callback;&lt;br /&gt;
&lt;br /&gt;
  callback = function () {&lt;br /&gt;
    OB.EventHandlerRegistry.callbackExecutor(view, form, grid, extraParameters, actions);&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  if (viewInGridMode) {&lt;br /&gt;
    grid.refreshGridFromClientEventHandler(callback);&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
OB.EventHandlerRegistry.register(OB.OBPFCI.COUNTRY_HEADER_TAB, OB.EventHandlerRegistry.POSTSAVE, OB.OBPFCI.ClientSideEventHandlers.refreshGrid, 'OBPFCI_RefreshGrid');&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that we are making use of a function of the grid called ''refreshGridFromClientEventHandler'', that is a special grid refresh method adapted to be used within this type of actions.&lt;br /&gt;
&lt;br /&gt;
This way, we are forcing the grid in the header of the [http://wiki.openbravo.com/wiki/Country_and_Region Country and Region] window to be refreshed every time a new record is created/updated on it by using the grid view.&lt;br /&gt;
&lt;br /&gt;
=== Pre-save Action: Client Validation ===&lt;br /&gt;
&lt;br /&gt;
In this example, we are checking a user's e-mail '''before''' saving/updating a record.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
OB.OBPFCI.USER_HEADER_TAB = '118';&lt;br /&gt;
OB.OBPFCI.ClientSideEventHandlers.validateEmail = function (view, form, grid, extraParameters, actions) {&lt;br /&gt;
  var data = extraParameters.data,&lt;br /&gt;
      emailPattern = /^\w+([\.\-]?\w+)*@\w+([\.\-]?\w+)*(\.\w{2,3})+$/;&lt;br /&gt;
&lt;br /&gt;
  if (data.email &amp;amp;&amp;amp; !emailPattern.test(data.email)) {&lt;br /&gt;
    view.messageBar.setMessage(isc.OBMessageBar.TYPE_ERROR, 'Invalid Email', 'The email address ' + data.email + ' is not valid');&lt;br /&gt;
    return; // Interrupting save action: not calling OB.EventHandlerRegistry.callbackExecutor&lt;br /&gt;
  }&lt;br /&gt;
  OB.EventHandlerRegistry.callbackExecutor(view, form, grid, extraParameters, actions);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
OB.EventHandlerRegistry.register(OB.OBPFCI.USER_HEADER_TAB, OB.EventHandlerRegistry.PRESAVE, OB.OBPFCI.ClientSideEventHandlers.validateEmail, 'OBPFCI_ValidateEmail');&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that if the e-mail is not valid, we do not call ''OB.EventHandlerRegistry.callbackExecutor'' so the save operation will '''not''' be performed.&lt;br /&gt;
&lt;br /&gt;
=== Pre-save Action: Calling Server Side ===&lt;br /&gt;
&lt;br /&gt;
In this case we are going to call a server side action '''before''' saving a [http://wiki.openbravo.com/wiki/Goods_Shipment Goods Shipment] line. To understand this example is important to know the concept of [http://wiki.openbravo.com/wiki/Openbravo_3_Architecture#ActionHandler:_server_side.2C_calling_from_the_client Action Handler].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
OB.OBPFCI.GOODS_SHIPMENT_LINES_TAB = '258';&lt;br /&gt;
OB.OBPFCI.ClientSideEventHandlers.checkStorageBin = function (view, form, grid, extraParameters, actions) {&lt;br /&gt;
  var data = extraParameters.data,&lt;br /&gt;
      callback, storageBin;&lt;br /&gt;
&lt;br /&gt;
  if (data.storageBin) {&lt;br /&gt;
    storageBin = data.storageBin;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  callback = function (response, cdata, request) {&lt;br /&gt;
    var row, stack, level;&lt;br /&gt;
    if (cdata &amp;amp;&amp;amp; cdata.storageBinInfo) {&lt;br /&gt;
      row = cdata.storageBinInfo.row;&lt;br /&gt;
      stack = cdata.storageBinInfo.stack;&lt;br /&gt;
      level = cdata.storageBinInfo.level;&lt;br /&gt;
      if (row !== '0') {&lt;br /&gt;
        view.messageBar.setMessage(isc.OBMessageBar.TYPE_ERROR, 'Invalid Storage Bin', 'Only storage bins with Row 0 are allowed');&lt;br /&gt;
        return; // Interrupting save action: not calling OB.EventHandlerRegistry.callbackExecutor&lt;br /&gt;
      }&lt;br /&gt;
      view.messageBar.setMessage(isc.OBMessageBar.TYPE_INFO, 'Shipment Line Saved', 'Storage Bin Info: Row ' + row + ', Stack ' + stack + ', Level ' + level);&lt;br /&gt;
      OB.EventHandlerRegistry.callbackExecutor(view, form, grid, extraParameters, actions);&lt;br /&gt;
    }&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  // Calling action handler&lt;br /&gt;
  OB.RemoteCallManager.call('org.openbravo.platform.ci.actionhandler.GoodsShipmentLinesActionHandler', {&lt;br /&gt;
    storageBinId: storageBin&lt;br /&gt;
  }, {}, callback);&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
OB.EventHandlerRegistry.register(OB.OBPFCI.GOODS_SHIPMENT_LINES_TAB, OB.EventHandlerRegistry.PRESAVE, OB.OBPFCI.ClientSideEventHandlers.checkStorageBin, 'OBPFCI_CheckStorageBin');&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above example calls to [https://code.openbravo.com/erp/mods/org.openbravo.platform.ci/file/be34511adfe7/src/org/openbravo/platform/ci/actionhandler/GoodsShipmentLinesActionHandler.java GoodsShipmentLinesActionHandler]. This action handler returns the row, stack and level of the storage bin whose id has been sent in the request. This id has been retrieved from the goods shipment line that we are about to save. &lt;br /&gt;
&lt;br /&gt;
The record will '''not''' be saved if the storage bin row is not 0. Otherwise, we show a message with the storage bin information.&lt;br /&gt;
&lt;br /&gt;
=== Pre-delete Action: Client Validation ===&lt;br /&gt;
&lt;br /&gt;
{{AvailableFrom|3.0PR17Q1}}&lt;br /&gt;
&lt;br /&gt;
In this case we are going to call a server side action '''before''' deleting a [http://wiki.openbravo.com/wiki/Sales_Order Sales Order] line. As in the previous example, to understand this one is important to know the concept of [http://wiki.openbravo.com/wiki/Openbravo_3_Architecture#ActionHandler:_server_side.2C_calling_from_the_client Action Handler].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
OB.CancelAndReplace.ClientSideEventHandlersPreDelete.showMessage = function (view, form, grid, extraParameters, actions) {&lt;br /&gt;
  var selectedRecords = extraParameters.recordsToDelete,&lt;br /&gt;
      record, replacementRecords = [],&lt;br /&gt;
      record, deliveredQuantity;&lt;br /&gt;
&lt;br /&gt;
  view.messageBar.keepOnAutomaticRefresh = true;&lt;br /&gt;
&lt;br /&gt;
  callback = function (response, cdata, request) {&lt;br /&gt;
    for (i = 0; i &amp;lt; cdata.result.length; i++) {&lt;br /&gt;
      record = cdata.result[i].record;&lt;br /&gt;
      deliveredQuantity = cdata.result[i].deliveredQuantity;&lt;br /&gt;
      if (deliveredQuantity !== 0) {&lt;br /&gt;
        var msgInfo = [];&lt;br /&gt;
        msgInfo.push(record.lineNo);&lt;br /&gt;
        msgInfo.push(record.product$_identifier);&lt;br /&gt;
        view.messageBar.setMessage(isc.OBMessageBar.TYPE_ERROR, null, OB.I18N.getLabel('CannotDeleteLineWithDeliveredQtyInReplacementLine', msgInfo));&lt;br /&gt;
        return;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    OB.EventHandlerRegistry.callbackExecutor(view, form, grid, extraParameters, actions);&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  if (view.getParentRecord().documentStatus === 'TMP') {&lt;br /&gt;
    for (i = 0; i &amp;lt; selectedRecords.length; i++) {&lt;br /&gt;
      record = selectedRecords[i];&lt;br /&gt;
      if (record.replacedorderline) {&lt;br /&gt;
        replacementRecords.push(record);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (replacementRecords.length) {&lt;br /&gt;
      //Calling action handler&lt;br /&gt;
      OB.RemoteCallManager.call('org.openbravo.common.actionhandler.CancelAndReplaceGetCancelledOrderLine', {&lt;br /&gt;
        records: replacementRecords&lt;br /&gt;
      }, {}, callback);&lt;br /&gt;
    } else {&lt;br /&gt;
      OB.EventHandlerRegistry.callbackExecutor(view, form, grid, extraParameters, actions);&lt;br /&gt;
    }&lt;br /&gt;
  } else {&lt;br /&gt;
    OB.EventHandlerRegistry.callbackExecutor(view, form, grid, extraParameters, actions);&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
OB.EventHandlerRegistry.register(OB.CancelAndReplace.SALES_ORDERLINES_TAB, OB.EventHandlerRegistry.PREDELETE, OB.CancelAndReplace.ClientSideEventHandlersPreDelete.showMessage, 'OBCancelAndReplace_ShowMessage');&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:HowTo]]&lt;/div&gt;</summary>
		<author><name>Wikiadmin</name></author>
		
	</entry>
</feed>