(7) Creating Actions

Overview

The seventh chapter explains the topic action handling in custom widgets.
This chapter is recommended for anyone planning to create a custom widget that can trigger actions.
The basis for chapter seven is a custom widget that uses the data assignment as it was explained and created in chapter three.

Widget Preview

The custom widget “Demo widget 7” will display the data of columns and rows just like “Demo widget 3”, but additionally support the execution of actions for certain triggers like widget mouse-events or a data point selection in the widget.
The screenshots below shows the final result of “Demo widget 7” ...

Resulting files

Demo widget 7 consists of the following directories and files.
As compared to the structure of "Demo widget 6" the following folders and files were added or changed

\---customWidgets
     \--- demoWidget7 
          |     demoWidget7Config.js 
          |     demoWidget7Module.js 
          | 
          +---assets 
          |     +---css 
          |     |         widget_demoWidget7.less  
          |     | 
          |     \---images 
          |               demoWidget7MenuIcon_32x32.png 
          | 
          +---js 
          |         demoWidget7AssignDataCtrl.js 
          |         demoWidget7Ctrl.js 
           |         demoWidget7DataService.js 
          | 
          \---partials 
               |   demoWidget7.html 
               | 
               \--- assignDataDialog
                     |   advancedProperties.html 

                     |   assignColumns.html 
                     |   assignData.html 
                     |   thresholdProperties.html 
                     | 
                     \---  columnTypeConfigTemplates 
                               date.html 
                               numeric.html 
                               text.html

The created files and source code accompanying this article can be downloaded here.

Creation steps

Step 1: Enable the action framework in the custom widget configuration

In order to use the action framework for a custom widget, it must be activated in the custom widget configuration.
Therefore please edit the custom widget configuration file demoWidget7Config.js and add the action "actions" as shown in the example below.

demoWidget7Config.js
angular.module('demoWidget7Module')
<span style="color:#0000FF;"><font><font>/ **</font></font>
 * Configuration for the custom widget
 * The config contains all configurable properties of the widget
 */</span>
.config([<span style="color:#008000;">'dashboardProviderProvider'</span>,<span style="color:#008000;">'columnTypeConfigUrlServiceProvider'</span>,
  <span style="color:#B22222;">  function </span>(dashboardProviderProvider, columnTypeConfigUrlServiceProvider) {
        dashboardProviderProvider.widget(<span style="color:#008000;">'demoWidget7'</span>, {
            title:<span style="color:#008000;"> 'Demo Widget 7'</span>,



actions: [

‘actions’,

]

Explanation:  Adding the action "actions" in the custom widget configuration (see line #15), will enable the widget for using the action framework.

As a result the widget properties menu will show the tab "Actions".

Explanation: The purpose of the "Actions"-view is the configuration of useful trigger-action-relationships for a widget.

Step 2: Defining the action-triggers for a custom widget

After enabling the action framework the action-triggers must be defined. Action-triggers are basically the user interactions with the custom widget. The most useful ones are mouse events, especially the selection of data points. All action-triggers are defined in the custom widget configuration. Therefore please edit the file demoWidget7Config.js again and add the desired action-triggers. The source code example below shows all supported action-triggers.

demoWidget7Config.js
angular.module('demoWidget7Module')
    /**
     * Configuration for the custom widget
     * The config contains all configurable properties of the widget
     */
    .config(['dashboardProviderProvider','columnTypeConfigUrlServiceProvider',
        function (dashboardProviderProvider, columnTypeConfigUrlServiceProvider) {
            dashboardProviderProvider.widget('demoWidget7', {
                title: 'Demo Widget 7',
...
                actions: [
...
                    'actions',
...
                ]
...
            actionFramework: {
                getActionTriggers: <span style="color:#B22222;">function</span>(widgetConfig) {
                   <span style="color:#B22222;"> var </span>supportedTriggers = [{<font><font>
                        Key: </font></font><span style="color:#008000;"><font><font>"onSelectionChange '</font></font></span><font><font> ,</font></font>
                        name: <span style="color:#008000;">'On selection change'</span><font><font>
                    }, {</font></font>
                        key: <span style="color:#008000;">'onMouseEnter'</span>,
                        name: <span style="color:#008000;">'On mouse over'</span><font><font>
                    }, {</font></font>
                        key: <span style="color:#008000;">'onClick'</span>,
                        name: <span style="color:#008000;">'On click'</span><font><font>
                    }];
                    </font></font><span style="color:#B22222;">return</span> supportedTriggers;<font><font>
                }
            }</font></font><font><font>

 

Explanation: The function "getActionTriggers(widgetConfig) starting in line #19 defines and returns an array of action-triggers for the custom widget. The array entries are simple pairs of action-key and action-name. The names of action-triggers will be displayed in the widget properties menu.

Explanation: After defining the action-triggers for a custom widget they can be selected in the "Trigger"-combo-box of the "Actions"-view.

Step 3: Extend the custom widget view with action-triggers

Up until this point it is possible to define triggers and actions for a custom widget. The defined triggers must now be implemented in the custom widget view.
Therefore please edit the custom widget view template demoWidget7.html and enrich the html with event listeners as in the example below:

demoWidget7.html
<!--This is the view template for Demo Widget 7-->
<div class="demoWidget7">
    <h3 ng-click="onClick()">Actions</h3>
    <div ng-show="columns.length == 0">No column was assigned.</div>
    <div ng-show="columns.length > 0">
        <div>Row count: {{rowCount}}</div>
        <table>
            <tr>
                <th ng-repeat="column in columns">
                    {{column.name}}
                </th>
            </tr>
            <tr ng-repeat="row in rows"
                class="rowEntry"
                ng-click="onRowClick(row)"
                ng-mouseover="onMouseOver(row)"
                ng-class="{'selected' : selectedRow === row}"
                ng-style="{'background-color' : getRowColor($index, row)}"
                >
                <td ng-repeat="column in columns">
...
                </td>
            </tr>
        </table>
    </div>
</div>

Explanation: 
The trigger "On click" is implemented in line #3 by adding an event listener for mouse-click-events to the widget headline element. This is realized using the AngularJS directive ng-click calling the scope function onClick().
The trigger "On selection change" is implemented in line #15 by adding an event listener for mouse-click-events to each row. This is done using the AngularJS directive ng-click calling the scope function onRowClick(row).
The trigger "On mouse move" is implemented in line #16 by adding an event listener for mouse-move-events to each row. This is implemented using the AngularJS directive ng-mouseover calling the scope function onMouseOver(row). 

Step 4: Extend the custom widget controller to handle action-triggers

The custom widget controller must be extended to provide the event listener functions that are called from the custom widget view and to trigger the action-framework on the defined user interactions. 
Therefore please edit the file demoWidget7Ctrl.js and add the following lines of code.

demoWidget7Ctrl.js
angular.module('demoWidget7Module')
<span style="color:#0000FF;"><font><font>/ **</font></font>
 * Controller for the custom widget
 * The controller provides the widget variables and manages the interactions between data-model and view
 */</span>
.controller(<span style="color:#008000;">'demoWidget7Ctrl'</span>,[<span style="color:#008000;">'$scope'</span>,<span style="color:#008000;">'formatDateService','formatNumberService','thresholdConstants','thresholdService'</span>,<span style="color:#008000;">'filterService'</span>,<span style="color:#008000;">'actionService'</span>,<span style="color:#008000;">'actionConstants'</span>,
    <span style="color:#B22222;">function</span>($scope, formatDateService, formatNumberService, thresholdConstants, thresholdService, filterService, actionService, actionConstants){<font><font>

        <span style="color:#0000FF;"><font><font>/ **</font></font>
         * This helper function informs the widget framework about selection changes
         * @param row The selected row
         * @returns true if the widget selection has changed, or false if it stays the same
         */</span>
       <span style="color:#B22222;"> var </span>handleRowSelection = function(row) {
            $scope.selectedRow = row;
        <span style="color:#B22222;">    var </span>columnNames = [];
            <span style="color:#B22222;">var</span> columnValues = [];
            fillRowDataArrays(row, columnNames, columnValues);
            <span style="color:#0000FF;">//filterService.onSelectionChange returns true only if the selection for this widget has really changed</span>
          <span style="color:#B22222;">  return</span> filterService.onSelectionChange($scope.item.identifier, columnNames, columnValues, $scope.cardIdentifier);<font><font>
        };</font></font>

        <span style="color:#0000FF;"><font><font>/ **</font></font>
         * This helper function informs the widget framework about triggered actions
         * @param row The row involved in the action
         * @param action The action to trigger
         */</span>
       <span style="color:#B22222;"> var</span> handleRowAction = <span style="color:#B22222;">function</span>(row, action) {
          <span style="color:#B22222;">  var</span> columnNames = [];
           <span style="color:#B22222;"> var </span>columnValues = [];<font><font>
            fillRowDataArrays(row, columnNames, columnValues);</font></font>
            actionService.triggerAction($scope.item.identifier, action, columnNames, columnValues);<font><font>
          };</font></font>

       <span style="color:#0000FF;"><font><font> / **</font></font>
         * This function is called when the user selects a row in the custom widget<font><font>
         * @param row The selected row</font></font>
         */</span>
        $scope.onRowClick = <span style="color:#B22222;">function</span>(row) {
          <span style="color:#B22222;">  var </span>selectionHasChanged = handleRowSelection(row);
            <span style="color:#B22222;">if</span>(selectionHasChanged){
                handleRowAction(row, actionConstants.actionTrigger.ON_SELECTION_CHANGE);<font><font>
            };</font></font><font><font>
        };</font></font>

       <span style="color:#0000FF;"><font><font> / **</font></font>
         * This function is called when the user hovers over a row in the custom widget
         * @param row The hovered row
         */</span>
        $scope.onMouseOver = <span style="color:#B22222;">function</span>(row) {
            handleRowAction(row, actionConstants.actionTrigger.ON_MOUSE_ENTER);<font><font>
        };</font></font>

        <span style="color:#0000FF;"><font><font>/ **</font></font>
         * This function is called when the user clicks on the widget title
         */</span>
        $scope.onClick =<span style="color:#B22222;"> function</span>() {
            actionService.triggerAction($scope.item.identifier, actionConstants.actionTrigger.ON_CLICK, [], []);<font><font>
        };</font></font><font><font>



initWidget ();
}
]);

Explanation: 
In line #7 and #8 the AngularJS services "actionService" and "actionConstants" were added to the controller. These services provide functionalities and constants necessary to trigger actions.
The scope function "onRowClick(row)", starting in line #41, handles mouse-click-events on custom widget rows. This function was introduced already in "Chapter 5 - Selection Handling" to handle a custom widget selection and is now extended to handle actions as well. Therefore it calls the helper function "handleRowAction(row, action)", see line #30, to trigger the actions configured to be run for the trigger "On selection change".
The scope function "onMouseOver(row)", starting in line #52, handles mouse-over-events on custom widget rows. It calls the helper function "handleRowAction(row, action)", see line #30, to trigger the actions configured to be run for the trigger "On mouse over".
The scope function "onClick()", starting in line #59, handles mouse-click-events on the custom widget headline element. As this trigger is not related to any custom widget row (data point) it calls the action service function "triggerAction(...)" without any selection data. This will trigger the actions configured to be run for the trigger "On click".

Summary and sources

As a final result of chapter seven, the custom widget supports the usage of actions now and can be configured to change a tab, set a selection or call an URL on defined user interactions (triggers).

Action configuration examples

1. Change to "Tab 2" on mouse click

2. Set the selection of other widgets when placing the mouse over custom widgets rows (data points)

3. Call an URL on a selection change in the custom widget

The source code for "Demo Widget 7" can be downloaded here.

Read in this series: