Mashing up Angular based UI into IPP Portal

This tutorial describes the steps to mash up Angular based UI into the IPP Portal. It describes how to ensure that data entered on the Angular UI is saved in the IPP data and the IPP data can be read and displayed in the Angular UI mashed up in the IPP Portal.

You can import the example model and code provided by Infinity. Therefore download the following ZIP file containing the UI Mashup example model as well as the required HTML and JavaScript code:

all-tutorials.zip

You find the sources for this tutorial in the mashup folder of the ZIP file.

Preparing the UI Mashup Environment

The following instruction shows how to prepare the UI Mashup Example environment by using the sources provided in the downloaded ZIP file:

  1. Install node.js from http://nodejs.org/.
  2. Create a folder for the external Web application project.
  3. Go to this folder and run the following command:
    npm install http-server

    Run install http-server

    This creates a new folder node_modules and performs a local install of an http-server in the external Web application project folder.
  4. Go to folder node_modules/http-server/public and create a folder called js.
  5. Add the following files to this folder: You can download these from the Internet.

    js folder

    To learn more about angular go to http://docs.angularjs.org/tutorial/
  6. Add the showDispute.html page from the example ZIP file to the node_modules/http-server/public folder.

    Add HTML file

  7. To enable cross-origin resource sharing, edit the web.xml file and add the following entry for the CorsFilter:
    <filter>
       <filter-name>CorsFilter</filter-name>
       <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
       <init-param>
          <param-name>cors.allowed.origins</param-name>
          <param-value>*</param-value>
       </init-param>
       <init-param>
          <param-name>cors.allowed.methods</param-name>
          <param-value>GET,POST,HEAD,OPTIONS,PUT</param-value>
       </init-param>
       <init-param>
          <param-name>cors.allowed.headers</param-name>
          <param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
       </init-param>
       <init-param>
          <param-name>cors.exposed.headers</param-name>
          <param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value>
       </init-param>
       <init-param>
          <param-name>cors.support.credentials</param-name>
          <param-value>true</param-value>
       </init-param>
       <init-param>
          <param-name>cors.preflight.maxage</param-name>
          <param-value>10</param-value>
       </init-param>
    </filter>
    <filter-mapping>
       <filter-name>CorsFilter</filter-name>
       <url-pattern>/*</url-pattern>
    </filter-mapping>
    Ensure that it is listed as the last filter!
  8. Start your server and deploy model UIMashupModel.xpdl, which you also find in the example ZIP file. It assumes that the external Web application is deployed at http://localhost:8000/showDispute.html. Change this URL if you are running your http server on a different host or port.

Having a closer Look at the Resources

The resources in the ZIP file provide code to mash up angular UI that will exhibit the reading and writing of data to IPP data.

If there is already data present in an IPP data element that the mashed up UI relates to, it will read that data from IPP over a RESTFUL service, show it in itself and allows editing as well. Once the data is edited and the Save and Complete button is clicked, it will write the edited data back to the IPP data element by calling another RESTFUL service of IPP.

Styling is provided in a way that the background color in the input field should change to light green in case the entry is valid and to pink if the entry is invalid.

showDispute.html

<html ng-app="ippUIMashup">

<head>
    <style>
        div.ex {
            width: 768px;
            padding: 10px;
            border: 2px solid gray;
            margin-left: 10px;
            border-style: outset;
        }
        
        .css-form input.ng-invalid.ng-dirty {
            background-color: pink;
        }
        
        .css-form input.ng-pattern.ng-dirty {
            background-color: blue;
        }
        
        .css-form input.ng-valid.ng-dirty {
            background-color: lightgreen;
        }
        
        .fourBy20 {
            width: 250px;
            height: 35px;
        }
    </style>
    <script type="text/javascript">
        var urlParam = function(name) {
            var results = new RegExp('[\\?&]' + name + '=([^&#]*)').exec(window.location.href);
            if (!results) {
                return 0;
            }
            return results[1] || 0;
        };
        var processPortalClient = function() {
            var url = urlParam('ippPortalBaseUri') + "/plugins/processportal/IppProcessPortalClient.js";
            console.log("url: " + url);
            if (url) {
                var script = document.querySelector("script[src*='" + url + "']");
                if (!script) {
                    var heads = document.getElementsByTagName("head");
                    if (heads && heads.length) {
                        var head = heads[0];
                        if (head) {
                            script = document.createElement('script');
                            script.setAttribute('src', url);
                            script.setAttribute('type', "text/javascript");
                            //if (charset) script.setAttribute('charset', charset);
                            head.appendChild(script);
                        }
                    }
                }
                return script;
            }
        }
    </script>
    <script src="js/angular.js">
    </script>
    <script src="js/angular-resource.js">
    </script>
    <script src="js/xml2json.js">
    </script>
    <script src="js/showDispute.js">
    </script>
</head>

<body ng-controller="disputeCtrl" onload="processPortalClient()">
    <div ng-form name="masterForm" class="css-form">
        <div class="ex">
            Dispute ID
            <input type="text" ng-model="Dispute.disputeID" ng-disabled="true">
            <br/>
            <br/> Dispute Description
            <textarea ng-class="{fourBy20: true}" ng-Model="Dispute.description" name="remarks" type="text" ng-maxlength="50"></textarea>
            <br/>
            <br/>
            <button ng-click="saveDispute()">Save and Submit</button>
        </div>
    </div>
</body>

</html>

showDispute.js

'use strict'
var ippUIMashup = angular.module('ippUIMashup', ['ngResource']);

ippUIMashup.config(['$httpProvider', function($httpProvider) {
    $httpProvider.defaults.useXDomain = true;
    $httpProvider.defaults.headers.put['Content-Type'] = 'application/xml';
}]);
ippUIMashup.controller('disputeCtrl', ['$scope', '$http', '$window',
    function($scope, $http, $window) {

        $scope.$window = $window;

        $scope.Dispute = {};
        $scope.MemberData = {};
        var x2js = new X2JS();
        $scope.urlParam = function(name) {
            var results = new RegExp('[\\?&]' + name + '=([^&#]*)').exec(window.location.href);
            if (!results) {
                return 0;
            }
            return results[1] || 0;
        }

        $scope.saveDispute = function() {
                var disputeData = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Dispute>" + x2js.json2xml_str($scope.Dispute) + "</Dispute>";
                console.log("Dispute to Submit: " + disputeData);
                /*This section of scripts write data back to IPP from $scope disputeData*/
                var res1 = $scope.callbackURL + '/outData/Dispute';
                $http.put(res1, disputeData).
                success(function(data, status, headers, config) {
                    console.log("Success");
					$scope.sleep(3000);
					IppProcessPortalClient.completeActivity();
                }).
                error(function(data, status, headers, config) {
                    $scope.error = true;
                    console.log("Error");
                });
			}	
            
			/**
             * Delay for a number of milliseconds
             */
        $scope.sleep = function(delay) {
            var start = new Date().getTime();
            while (new Date().getTime() < start + delay);
        }


        $scope.sleep(1000);

        $scope.callbackURL = $scope.urlParam("ippInteractionUri");
        //To ensure that $resource maintains port no.
        if ($scope.callbackURL) {
            $scope.callbackURL = $scope.callbackURL.replace("\\", "");
        }
        console.log($scope.callbackURL);

        /*This section of scripts fetches data from IPP and set it to $scope data*/
        var res2 = $scope.callbackURL + '/inData/Dispute';
        $http.get(res2).
        success(function(data, status, headers, config) {
            console.log("Data is:" + data);
            var json = x2js.xml_str2json(data);
            console.log("JSON Data is:" + json.Dispute.disputeID);
            $scope.Dispute.disputeID = json.Dispute.disputeID;
            $scope.Dispute.description = json.Dispute.description;
        }).
        error(function(data, status, headers, config) {
            $scope.error = true;
        });
    }
]);

Having a closer Look at the IPP Model

The provided UIMashupModel.xpdl model has the following elements:

The Structured Data Type

The structured type has the fields disputeID and description as shown in the following screenshot:

Structured Type for Dispute
Figure: Structured Type for Dispute

The UI Mashup Application

The UI Mashup application has a parameter Dispute with In/Out direction and structured data Dispute. You can see this parameter in the Parameters tab of the properties page of the application.

UI Mashup Parameter
Figure: UI Mashup Parameter

In the Configuration tab you see the added URL http://localhost:8000/app/showDispute.html.

URL configuration
Figure: URL configuration

The Message Transformation

A Message Transformation application serves to add default values to the Dispute data. In the Configuration tab of the property page of the Message Transformation, you find the default values mapped to the Dispute elements.

Mapping values of the Message Transformation
Figure: Mapping values of the Message Transformation

The Process

The process contains the following elements:

  1. A Start event followed by a Script Task implementing the Message Transformation application
  2. A User task implementing the UI Mashup application
  3. An Out-data mapping from the Message Transformation to the Dispute data. The Output Access Point from the Message Transformation is pointing to the data accordingly:

    Output Access Point

  4. An In/Out-data mapping from the UI Mashup Application to the Dispute data. The parameter Dispute is selected as Input as well as Output Access Point.

    Input and Output Access Points

  5. An Out-data mapping from Dispute data to the manual task, which displays the edited data

Completed Process
Figure: Completed Process

Running the Example

Now we will start both Web servers and check whether the Infinity UI Mashup application is able to mashup both UIs and read and write back data.

Starting the HTTP Server

Go to folder http-server and execute the command

node ./bin/http-server -a 0.0.0.0 -p 8000

This will start up the http server and listens for connections on port 8000.

Starting the HTTP server
Figure: Starting the HTTP server

You can check if is running by accessing http://localhost:8000/showDispute.html. The page should look similar to the following:

Angular UI Test
Figure: Angular UI Test

Starting the IPP Process

Switch to the Workflow Execution perspective and start the process.

Start the process
Figure: Start the process

The mashed up Angular UI is displayed with the default values given by the Message Transformation application in the IPP data, fetched over the IPP RESTFUL service. Change a value and click the Save and Submit button.

Edit the data
Figure: Edit the data

The IPP RESTFUL service writes the data back to the IPP data element. The activity completes and the manual task is opening displaying the edited data.

Manual Task displaying the edited data
Figure: Manual Task displaying the edited data

Now we successfully read and wrote data from Angular based UI from and to IPP!