Skip to main content
Jump to: navigation, search

Stardust/Knowledge Base/Integration/UI/UIMashup/AngularJS

In this article we will be developing a user interface using HTML and AngularJS. We will also see some of the tricks of Angular to quickly and smartly get things done, like usage of, ng-repeat, ng-show, ng-include etc. We will also see how to write services in AngularJs to consume RESTFul services. We will see the usage of filter in easing out user's experience when it comes to locate an option out of a very long options list.


Requirements

The requirement is broadly explained below:

  1. Need to automatically populate the 2nd select box (Dispute Type) based on the selection made in the first select box (Source Of Dispute).
  2. Based on the selection of the 2nd selection, respective html file to be included under the section “Additional Details”.
  3. The button Fetch Customer Details to be only activity if there is something entered in the Enter Customer Account Number input box.
  4. When user starts entering data in ceratin input field the background color should change to light green incase entry is valid and to pink if the entry is invalid.
  5. The table to show the fetched customer data if the customer is found else table will enable input texts to enter data manually.
  6. The Account Type radios can also be selected by clicking on the labels it presents (Major/Minor).
  7. To ensure that none of the validations are missed, even if they are defined in the getting included html (based on dispute type selection) and Submit Form button is only enabled if the entire form is valid.
  8. You have list of branch names and associated branch code, IFSC code and served ATMs out of these branches. To ease users, you wish to filter the branch name based on user’s entry in an input text and accordingly fetch branch code and IFSC code and at the same time accordingly populating the ATM select box, based on selected branch name.
  9. You want to give tooltips on mouse hover to controls (input or even complete div).
  10. You need to fetch certain data over RESTFul services running seperately on your page, like list of transactions or sequence (random) to make a dispute ID e.g. <<year of today’s date>>-<<month of today’s date>>-<<date of today’s date>>-<<random sequence fetched over RESTFUL service>>
  11. You need to make a table selectable, i.e. on click on a paricular row, that row data gets picked and you can work with the selected row data.

Getting ready

Install node.js from http://nodejs.org/
Get a working application from https://github.com/angular/angular-phonecat and download it as zip. This is the folder where you will place your html and js files.
To learn more about angular go to http://docs.angularjs.org/tutorial/

Running the example

Open node.js command prompt, go to the directory where you have unzipped angular-phonecat application and run

node scripts\web-server.js

Place the firstExample.html at app folder under the folder angular-phonecat-master and firstExample.js under app\js.
Hit http://localhost:8000/app/firstExample.html


Code

HTML

firstExample.html
<!doctype html>
<html ng-app="newRESTClient">
  <head>
 
    <style>
 
      .leftPane{
      background-color:#FFD700;height:800px;width:250px;float:left; margin-right:5px;
    }
    .redBg{
      background-color: tomato;
    }
    .greenBg{
      background-color: lightgreen;
    }
    .greyBg{
      background-color: grey;
    }
    .blueFont{
      color: blue;
    }
    .selected {
        background-color: lightgreen;
      }
   .footer{
      background-color:lightgreen;clear:both;text-align:center; 
    }
div.ex
{
width:640px;
padding:10px;
border:2px solid gray;
margin-left:260px;
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 src="lib/angular/angular.js"> </script>
    <script src="lib/angular/angular-resource.js"> </script>
    <script src="js/firstExample.js"> </script>
  </head>
  <body ng-controller="firstExampleCtrl">
 
  <div class="selected">
      <table>
        <tr>
          <td style="width: 25%;">
            <img ng-src="img\MyPlot.jpg">
          </td>
          <td class="alignTop" style="width: 70%;">
            <h2 style="margin-bottom:0;">Dispute Entry Page</h2>
 
                <marquee behavior="scroll" direction="left" scrollamount="8" 
		onmouseover="this.setAttribute('scrollamount', 3, 0);" 
		onmouseout="this.setAttribute('scrollamount', 8, 0);">
                <a href="pos01.html" target="_blank" style="text-decoration: none;">
		Credit Cards with <mark style="bakground-color: yello;">0 joining fees.</a>
                <a href="http://www.latestrates.html" target="_blank" 
			style="text-decoration: none;">Home loans are @ 9.95%.</a>
                  <a href="http://www.saraswatbank.com/view_section.jsp?lang=0&id=0,88" target="_blank" 
			style="text-decoration: none;">Latest FD rates are out, Click here to view those.</a>
                </marquee>
 
          </td>
          <td>
            <a href="http://www.google.com" target="_blank">Logout</a>
          </td>
          <td style="text-align:right;">
            <a href="http://www.angularjs.org" target="_blank">Help</a>
          </td>
        </tr>
      </table>
    </div>
 
    <!--Start: Responsible to acheive 1 and 2 of the requirement-->
    <div id="menu" class="leftPane">
      Source of Dispute<select ng-model="disputeSource" ng-options="t.name for t in disputeSourceOptions">
 
      </select>
      <br>
      <span ng-show="disputeSource">
      {{disputeSource.name}} Dispute Type<select ng-model="disputeType" 
	ng-options="t.name for t in disputeSourceOptions[disputeSource.id].options" >
 
      </select>
 
    </span>
  </div>
  <!--Ends: Responsible to acheive 1 and 2 of the requirement-->
<!-- qualfifying the form with css-form class does the trick of changing the background 
color of the component when data is getting typed into those, based on validity of 
the data the color changes from lightgreen(valid) to pink (invalid). Achieves requirement number 4-->
    <div ng-form name="masterForm" class="css-form">
 
 
         <h3><u>Customer Details</u></h3><br>
         <!--Start: required attribute and ng-dibaled of button ensures to achieve requirement 3-->
         <!-- title attribute enables us to show tooltips on the defined components, 
	like this and email. Implements requirement number 9-->
         Enter Customer Account Number <input type="text" name="custAct" ng-model="searchText" 
	required autofocus title="Card Or Account Number">
         Enter Customer Email <input type="email" name="custeMail" ng-model="Cust.email" 
	required title="Valid Email like a@a.com">
         <!--this div enables us to show error messages if the entered data in the component is not valid-->
         <div ng-show="masterForm.custeMail.$dirty && masterForm.custeMail.$invalid">Invalid:
          <span ng-show="masterForm.custeMail.$error.required">Tell us your email.</span>
          <span ng-show="masterForm.custeMail.$error.email">Not a valid email.</span>
        </div>
         <button ng-click="getCustomerDetails(searchText)"  ng-disabled="masterForm.custAct.$invalid">
	Fetch Customer Details</button>
         <!-- ng-show is responsible for showing or hiding the table based on the returned 
	result of the serach function. Acheives requirement number 5-->
        <table border="1" style="border:1px solid black; border-collapse:collapse;" ng-show="showTable">
 
       <tr><td>Customer Name</td><td>Customer Address</td><td>Customer ZIP</td><td>Customer DOB</td></tr>
 
 
        <tr>
        <td>{{Cust.name}}</td><td>{{Cust.address}}</td><td>{{Cust.zip}}</td><td>{{Cust.dob}}</td>
 
      </tr>
    </table>
      <table border="1" border="1" style="border:1px solid black; border-collapse:collapse;
	 -webkit-border-radius:13px;-moz-border-radius:13px;-ms-border-radius:13px;
	-o-border-radius:13px;border-radius:13px;" ng-show="!showTable">
 
       <tr><td>Customer Name</td><td>Customer Address</td><td>Customer ZIP</td><td>Customer DOB</td></tr>
 
 
        <tr>
        <td><input type="text" ng-model="Cust.name"></td>
        <td><input type="text" ng-model="Cust.address"></td>
 
        <!-- pattern to only accept digits: ng-pattern="/^[0-9]+$/" -->
        <td><input type="text" name="zip" ng-pattern="/^[0-9]+$/" ng-model="Cust.zip" required>
          <span ng-show="masterForm.zip.$error.pattern">Invalid: Only Digits.</span>
        </td>
        <td><input type="text" ng-model="Cust.dob"></td>
 
      </tr>
    </table>
    <!--having label defined ensures we get requirement number 6 implemented-->
    Account Type <input type="radio" name="acctType" id="minor" ng-model="Cust.acctType" value="minor">
      <label for="minor">Minor</label>
 
    <input type="radio" name="acctType" id="major" ng-model="Cust.acctType" value="major">
    <label for="major">Major</label>
    <hr>
    <div class="ex">
      Dispute ID <input tpye="text" ng-model="Dispute.disputeID" ng-disabled="true">
      Dispute Description <textarea ng-class="{fourBy20: true}" ng-Model="Dispute.description" 
	name="remarks" type="text" ng-maxlength="50"></textarea>
    <span class="error" ng-show="masterForm.remarks.$error.maxlength">Shuold not be more than 50 characters</span>
 
    </div>
 
 
 
<hr>
<h3><u>Additional Details</u></h3>
<!-- ng-include and disputeType.file (which comes from the disputeSource array 
	ensures we get proper file name), part of requirement number 2.-->
      <div class="ex" ng-include src="disputeType.file" ng-show="disputeType" ></div>
 
      </div>
 
      <hr>
 
      <div>
        <h3><u>Transaction Details</u></h3>
         <button ng-click="searchDetails()">Search Transactions for - {{searchText}}</button>
          <!-- Chosen date is: {{Customer.transDate | date: 'dd/MM/yyyy'}} --> 
          <div>
          <table border="1" width="800">
            <thead>
              <td>Transaction ID</td>
              <td>Card Number</td>
              <td>Transaction Date</td>
              <td>Transaction Amount</td>
            </thead>
            <!-- ng-Click with $index, which gets the seleted row index in ng-class definintion does the trick.
		 Implements trequirement number 11-->
            <tr class="selectable" ng-click="onClick($index)" ng-repeat="transaction in transactions.transaction"
		 ng-class="{selected: $index==selectedRow}">            
              <td>{{transaction.id}}</td>
              <td>{{transaction.cardNo}}</td>
              <td>{{transaction.transDate}}</td>
              <td>{{transaction.transAmount}}</td>  
            </tr>
          </table>
          </div>
          <div>
            <b>Selected Transaction:</b><br>
            <table border="1" width="800" class="greyBg" ng-show="selectedRow >=0">
            <thead>
              <td>Transaction ID</td>
              <td>Card Number</td>
              <td>Transaction Date</td>
              <td>Transaction Amount</td>
            </thead>
            <tr>            
              <td>{{selectedTransaction.id}}</td>
              <td>{{selectedTransaction.cardNo}}</td>
              <td>{{selectedTransaction.transDate}}</td>
              <td>{{selectedTransaction.transAmount}}</td>  
            </tr>
          </table>
           <!--  Transaction ID<input type="text" ng-model="selectedTransaction.id">
            Card Number<input type="text" ng-model="selectedTransaction.cardNo">
            Transaction Date<input type="text" ng-model="selectedTransaction.transDate">
            Transaction Amount<input type="text" ng-model="selectedTransaction.transAmount"> -->
          </div>
        </div>
        <!-- ng-disabled on masterForm.$invalid ensured that the button is only enabled if the form is valid.
        Implements requirement number 7.-->
    <button ng-click="submitForm()"  ng-disabled="masterForm.$invalid">Submit Form</button>
 
    <div id="footer" class="footer">Copyright  © www.sungard.com <nav style="text-align:right;">
<a href="pos02.html" target="_blank">Call Us</a> | 
<a href="atm02.html" target="_blank">Write To Us</a> | 
 
</nav></div>
 
  </body>
</html>


atm01.html (included in firstExample.html):
<!doctype html>
 
    <div ng-form name="atm01Form" title="Locate the branch, rest of the data will be fetched automatically">
 
      <!-- the query data and the filter applied to the branchName select box is 
	responsible to shorten the list of branchNames based on typed data in query input text-->
      <!-- and then based on selectedBranch we smartly pick the rest of the data 
	and also get only relevant atm options in the ATM select box. Implements requirement number 8-->
      Quickly Find Branch Details <input type="text" ng-model="query">
 
      <br>
 
      <!-- Please note the $parent prefix, it allows you to access data in parent
	 controller scope, by default ng-include creates a child scope and any 
	editing in data will not get directly pushed up in parent controller. 
	Took me 3 hrs to understand and fix it-->
      Branch Name <select  name="branchName" ng-model="$parent.selectedBranch" 
	ng-options="p.name for p in branchDetails | filter: query" required>
 
      <span ng-show="atm01Form.branchName.$error.required">Branch needs to be selected.</span>
        <option>Select</option>
 
      </select>
 
 
      Branch Code is: {{branchDetails[selectedBranch.id].code}}
      <!--select  ng-model="selectedBranchCode" ng-options="p.code for p in branchDetails">
        <option ng-repeat="p in branchDetails[selectedBranch.id].code">{{p.code}}</option>
 
      </select-->
 
      IFSC Code is: {{branchDetails[selectedBranch.id].ifsccode}}
      <!--select  ng-model="selectedBranchIFSCCode">
      <ng-options="p.ifsccode for p in branchDetails">
        <option ng-repeat="p in branchDetails[selectedBranch.id].ifsccode">{{p.ifsccode}}</option>
 
      </select-->
 
 
      <br>
 
 
      Identify and Select ATM <select  ng-model="$parent.selectedATMCode">
              <option ng-repeat="p in branchDetails[selectedBranch.id].atms" value="{{p.id}}">
	{{p.name}}-{{p.landmark}}</option>
      </select>
 
      <!--button ng-click="buttonClicked()">Click Me!</button-->
 
    </div>

Angular Java Script

firstExample.js
var newRESTClient = angular.module('newRESTClient', ['ngResource']);
newRESTClient.
    factory('getCounter', ['$resource', function($resource) {
        return $resource('http://localhost\\:8080/MyFisrtRESTService/pretech/counter', {}, {
    'get': {method:'GET', params:{}, isArray:false}
  }); 
 
    }]);
 
    newRESTClient.
    factory('getTransactions', ['$resource', function($resource) {
        return {
            getStateDetailsByCode: function(cardNo) {
            	//console.debug("Passed arg: "+ cardNo);
                return $resource('http://localhost\\:8080/MyFisrtRESTService/pretech/cardNo?cardNo=:cardNo').get(
                {
                    cardNo: cardNo
                },
                 function (data) {   //success
                        console.debug(data.transaction[0]);
                    if (! (data['transaction'] instanceof Array)){
                    	console.debug("IsArray");
                        if (typeof data['transaction'] != 'undefined'){ 
                            var myArray = new Array();
                            myArray.push(data['transaction']);
                            data['transaction'] =  myArray;   
                            console.debug("Array:"+myArray[0]);        
                        }                                                       
                    }
                },
                function (data) {   //failure
                    //error handling goes here
                    }
 
                )
            }
        }
    }]);
 
var todayDate = new Date();
 
function firstExampleCtrl($scope, $resource, getCounter, getTransactions){
	/*declare the array like this so that we can use it for both the select boxes.
	Ensure to have id defined in consireation of typicall Array indexing in mind
	This declaration ensures we can achieve requirement 1 and 2 easily and efficiently*/
	$scope.disputeSourceOptions = [
	{"id": "0", "name": "ATM",
	"options": [
		{"id": "atm01", "name": "Cash Not Disbursed", "file": "atm01.html"},
		{"id": "atm02", "name": "Debited Twice", "file": "atm02.html"}
		]
	},
	{"id": "1", "name": "PoS",
	"options":[
		{"id": "pos01", "name": "Card swipped twice", "file": "pos01.html"},
		{"id": "pos02", "name": "I didn't autorize", "file": "pos02.html"}
		]
	},
	{"id": "2", "name": "IMT",
	"options": [
		{"id": "imt01", "name": "Account Debited without transaction", "file": "imt01.html"}
		]
	},
	{"id": "3", "name": "Internet Banking", 
	"options":[
		{"id": "inetbnk01", "name": "Service Not Received", "file": "inetbnk01.html"},
		{"id": "inetbnk02", "name": "Faulty Product", "file": "inetbnk02.html"}
		]
	}
	];
 
 
	$scope.Cust ={};
	$scope.Dispute ={};
	$scope.showTable = false;
	//Master List of Customers, getCustomerDetails function uses this to compare and return matching customer data
	$scope.Customers = [
	{"cardNo": "1001", "name": "1ABC", "address":"Pune_1", "zip":"411027", "dob": "21/12/1987", "acctType" : "minor"},
	{"cardNo": "1002", "name": "2ABC", "address":"Pune_2", "zip":"411028", "dob": "21/12/1985", "acctType" : "minor"},
	{"cardNo": "1003", "name": "3ABC", "address":"Pune_3", "zip":"411029", "dob": "21/12/1986", "acctType" : "major"},
	{"cardNo": "1004", "name": "4ABC", "address":"Pune_4", "zip":"411022", "dob": "21/12/1983", "acctType" : "major"},
	{"cardNo": "1005", "name": "5ABC", "address":"Pune_5", "zip":"411021", "dob": "21/12/1988", "acctType" : "major"}
	];
 
	$scope.branchDetails = [
	{	"id" : "0",
		"code": "BR001", "name": "Andheri", "address":"Andheri Mumbai", "zip":"400027", "ifsccode": "AXIS0000179",
	"atms": [
		{"id": "0", "code": "ATM001", "name": "Andheri East1", "landmark":"Raj Hospital", "zip":"400027"},
		{"id": "1", "code": "ATM002", "name": "Andheri East2", "landmark":"Ganpati Temple", "zip":"400027"}
		]
	},
	{	"id" : "1",
		"code": "BR002", "name": "Kurla", "address":"Kurla Mumbai", "zip":"400024", "ifsccode": "AXIS0000178", 
	"atms" :[
		{"id": "0"," code": "ATM005", "name": "Kurla", "landmark":"Kurla Theater", "zip":"400272" },
		{ "id": "1", "code": "ATM008", "name": "Kurla", "landmark":"Kurla ABC Office", "zip":"400272" }
		]
	},
	{	"id" : "2",
		"code": "BR003", "name": "NaviMumbai", "address":"NaviMumbai Mumbai", "zip":"400027", "ifsccode": "AXIS0000177",
	"atms" :[
		{"id": "0", "code": "ATM003", "name": "NaviMumbai", "landmark":"NaviMumbai Vada Pav", "zip":"400020" },
		{"id": "1", "code": "ATM006", "name": "NaviMumbai1", "landmark":"NaviMumbai AXIS Bank", "zip":"400020" }
		]
	},
	{	"id" : "3",
		"code": "BR004", "name": "Bandra", "address":"Bandra Mumbai", "zip":"400127", "ifsccode": "AXIS0000176",
	"atms" : [
		{"id": "0", "code": "ATM004", "name": "Bandra", "landmark":"Bandra Church", "zip":"400127" },
		{"id": "1", "code": "ATM007", "name": "Bandra1", "landmark":"BKC", "zip":"400127" }
		]
	},
	{	"id" : "4",
		"code": "BR005", "name": "Khargar", "address":"Khargar Mumbai", "zip":"400272", "ifsccode": "AXIS0000175",
	"atms" : [
		{"id": "0", "code": "ATM009", "name": "Khargar", "landmark":"Khargar Ground", "zip":"400007" }
		]
	},
	];
 
 
	$scope.selectedTransaction = {};
	$scope.selectedBranch = {};
	$scope.transactions = [];
	$scope.getCustomerDetails = function(searchText){
		//console.debug("Incoming: "+searchText);
		for (var i = 0; i < $scope.Customers.length; i++) {
			console.debug($scope.Customers[i].cardNo);
			if($scope.Customers[i].cardNo == searchText){
 
 
				$scope.Cust.cardNo = $scope.Customers[i].cardNo;
				$scope.Cust.name = $scope.Customers[i].name;
				$scope.Cust.address = $scope.Customers[i].address;
				$scope.Cust.zip = $scope.Customers[i].zip;
				$scope.Cust.dob = $scope.Customers[i].dob;
				$scope.Cust.acctType = $scope.Customers[i].acctType;
				$scope.showTable = true;
				console.debug("Matched");
				break;
			}
			else
			{
 
				$scope.Cust.cardNo = $scope.searchText;
				$scope.Cust.name = "";
				$scope.Cust.address = "";
				$scope.Cust.zip = "";
				$scope.Cust.dob = "";
				$scope.Cust.acctType = "";
				$scope.showTable= false;
			}
			/*call REST service to fetch counter and set it in scope data (Dispute.disputeID)
			thsi section also checks the returned data and if its single digit, prefixes it with
			2 zeros and if its double digit, prefixes it with 1 zero. Implements requirement number 10*/
			getCounter.get(function(data) {
				if(data.counter <=9){
					data.counter = "00"+data.counter;
				}
				else if(data.counter >9 && data.counter <=99)
				{
					data.counter = "0" + data.counter;
				}
	    		$scope.Dispute.disputeID = todayDate.toJSON().slice(0,10) + "-"+data.counter;
	    		console.debug($scope.Dispute.disputeID);
	  			});
		};
		console.debug("Table Show: " + $scope.showTable);
 
 
 
}
$scope.submitForm = function(){
 
		console.debug("Customer Data to Submit: " + angular.toJson($scope.Cust));
		console.debug("Transaction Data to Submit: " + angular.toJson($scope.selectedTransaction));
		console.debug("Dispute Data to Submit: " + angular.toJson($scope.Dispute));
		console.debug("Branch Data to Submit: "+ angular.toJson($scope.selectedBranch));
		console.debug("ATM Data to Submit: "+ angular.toJson($scope.selectedBranch.atms[$scope.selectedATMCode]));
}
/*This function calls REST service passing customer's card no as argument and returns the list of transactions.
Implements requirement number 10.*/
$scope.searchDetails = function() {                
                $scope.transactions = getTransactions.getStateDetailsByCode($scope.Cust.cardNo);
                //console.debug("Result: " +$scope.transactions.transaction);                  
                //console.debug("Inside Search: " + $scope.transactions.length);
} 
//This function is called when a row is clicked in transactions table, the index is used to get the selected row
//set its values into selectedTransaction object
$scope.onClick = function(index){
	$scope.selectedRow = index;
 
			angular.copy($scope.transactions.transaction[$scope.selectedRow], $scope.selectedTransaction);
	}	
 
 
 
}

Output

AngularHTML.JPG


How to read/write data from Angular based UI from/to IPP

In this section we will explore steps to mash up Angular based UI into IPP Portal and how do we ensure that data entered on Angular UI gets saved in IPP data and how we can read IPP data and show it in Angular UI mashed up in IPP Portal.

  • First lets create an xpdl, that will activities, which will mash up Angular UIs into it.AngularXPDL MyProcess Default.jpg
  • We will now need to edit the code shown above, so that it can save data entered onto Angular UI into IPP data (in thsi case we are saving 2 data elements, Cust1 and Dispute1). Changes are:
/* New Method
This method seraches and returns the desired query parameter (passed as arg) out of 
window.location.href, which contains entire URL like:
http://localhost:8000/app/firstExample.html?
 
ippInteractionUri=http://localhost:9090/AXISDemo/services/rest/engine/interactions/MTA1fDEzNzU5NDIzNTE4ODY=
 
&ippPortalBaseUri=http://localhost:9090/AXISDemo
 
&ippServicesBaseUri=http://localhost:9090/AXISDemo/services/
*/
$scope.urlParam = function(name){
    var results = new RegExp('[\\?&]' + name + '=([^&#]*)').exec(window.location.href);
    if (!results)
    { 
        return 0; 
    }
    return results[1] || 0;
}
 
$scope.submitForm = function(){
 
 
		var converter= new X2JS();
 
		var customerData = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>	
		<Cust1>"+converter.json2xml_str($scope.Cust)+"</Cust1>";
		var disputeData = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
		<Dispute1>"+converter.json2xml_str($scope.Dispute)+"</Dispute1>";
 
		$scope.callbackURL = $scope.urlParam("ippInteractionUri");
		//To ensure that $resource maintains port no.
		var newTemp = $scope.callbackURL.replace(/:/g, "\\:");
		$scope.callbackURL = newTemp;
		//*****writes dataID Cust1 to IPP*****
		var res = $resource($scope.callbackURL+'/outData/Cust1', {params: '@customerData'}, {'put': {method: 
  		'PUT',  isArray: false, headers:{'Content-Type':'application/xml; charset=UTF-8'}}});
  		res.put(customerData, 
  		function success() {console.debug("Success");}, function error() {console.debug("Failure");});
 
		console.debug("XML of Customer: "+ customerData);
		console.debug("XML of Dispute: "+ disputeData);
 
		$scope.sleep(6000);
 
		//*****write dataID Dispute1 to IPP****
		var res1 = $resource($scope.callbackURL+'/outData/Dispute1', {params: '@disputeData'}, {'put': {method: 
  		'PUT',  isArray: false, headers:{'Content-Type':'application/xml; charset=UTF-8'}}});
  		res1.put(disputeData, 
  		function success() {console.debug("Success");}, function error() {console.debug("Failure");});
  		$scope.sleep(6000);
  		//*****Ensures that on button click IPP's activity that contains this UI gets completed and closed****
		var mainIppFrame = parent;
			    //alert(mainIppFrame);
 
			    if (mainIppFrame)
			    {
			      if (mainIppFrame.InfinityBpm)
			      {
			 		mainIppFrame.InfinityBpm.ProcessPortal.completeActivity();
 
			      }
			    }
 
}
 
/** New Method
* Delay for a number of milliseconds
*/
$scope.sleep = function (delay)
{
    var start = new Date().getTime();
    while (new Date().getTime() < start + delay);
}
We introduced 2 new methods and made changes in submitForm() to call 2 RESTFUL services on IPP to write 2 data elements. It can be verified on the next activity, as its a manual activity that displays the data entered and saved in mashed up Angular UI.


The data you enter here......

Image1.JPG


is displayed here (meaning we could successfully wrote data from Angular UI into IPP data elements (Cust1 and Dispute1)

Image2.JPG

  • In the next activity we will mash up another angular UI, that will exhibit the reading and writing of data to IPP data. That means if there is already data present in IPP data element that this mashed up UI relates to, it will read that data from IPP over RESTFUL service and show it in itself and allows editing as well. Once edited and Save and Complete button clicked it will write edited data to IPP data element by calling another RESTFUL service of IPP. The code for this UI is here:
showDispute.html
<!doctype html>
<html ng-app="newRESTClient">
  <head>
 
    <style>
 
 
    div.ex
    {
    width:640px;
    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 src="lib/angular/angular.js"> </script>
    <script src="lib/angular/angular-resource.js"> </script>
    <script src="lib/misc/xml2js.js"> </script>
    <script src="js/showDispute.js"> </script>
</head>
  <body ng-controller="disputeCtrl">
 
      <div ng-form name="masterForm" class="css-form">
 
        <div class="ex">
          Dispute ID <input tpye="text" ng-model="Dispute.disputeID" ng-disabled="true">
          Dispute Description <textarea ng-class="{fourBy20: true}" ng-Model="Dispute.description" name="remarks" 
		type="text" ng-maxlength="50"></textarea>
          <span class="error" ng-show="masterForm.remarks.$error.maxlength">Shuold not be more than 50 characters</span>
          <button ng-click="saveDispute()">Save and Submit</button>
        </div>
      </div>
 
  </body>
</html>
showDispute.js
'use strict'
var newRESTClient = angular.module('newRESTClient', ['ngResource']);
function disputeCtrl($scope, $resource, $window){
 
	$scope.$window = $window;
 
	$scope.Dispute ={};
	var x2j = new X2JS();
	/* This method seraches and returns the desired query parameter (passed as arg) out of 
	window.location.href, which contains entire URL like:
	http://localhost:8000/app/firstExample.html?
	ippInteractionUri=http://localhost:9090/AXISDemo/services/rest/engine/interactions/MTA1fDEzNzU5NDIzNTE4ODY=
	&ippPortalBaseUri=http://localhost:9090/AXISDemo
	&ippServicesBaseUri=http://localhost:9090/AXISDemo/services/ */
	$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\"?><Dispute1>"+x2j.json2xml_str($scope.Dispute)+"
		</Dispute1>";
	    console.debug("Dispute to Submit: "+ disputeData);
	    var res1 = $resource($scope.callbackURL+'/outData/Dispute2', {params: '@disputeData'}, {'put': {method: 
  		'PUT',  isArray: false, headers:{'Content-Type':'application/xml; charset=UTF-8'}}});
  		res1.put(disputeData, 
  		function success() {console.debug("Success");}, function error() {console.debug("Failure");});
  		$scope.sleep(3000);
  		//*****Ensures that on button click IPP's activity that contains this UI gets completed and closed****
		var mainIppFrame = parent;
			    //alert(mainIppFrame);
 
			    if (mainIppFrame)
			    {
			      if (mainIppFrame.InfinityBpm)
			      {
			 		mainIppFrame.InfinityBpm.ProcessPortal.completeActivity();
 
			      }
			    }
	}
	/**
	* 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){
		var newTemp = $scope.callbackURL.replace(/:/g, "\\:");
	}
		$scope.callbackURL = newTemp;
 
		/*This section of scripts fetches data from IPP and set it to $scope data*/
		var res2 = $resource($scope.callbackURL+'/inData/Dispute1', {}, {'get': {method: 
  		'GET', transformResponse: function(data) {
  			console.debug("Data is:" + data);
		var json = x2js.xml_str2json(data );
		console.debug("JSON Data is:" + json.Dispute.disputeID);
		$scope.Dispute.disputeID = json.Dispute.disputeID;
		$scope.Dispute.description = json.Dispute.description;		
		}, isArray: false}});
  		res2.get(function success(data) {console.debug("Success");}, function error() {console.debug("Failure");});
 }

The UI:

Here we see the IPP data element into Angular UI by fetching it over IPP RESTFUL service

Image3.JPG


We edit Dispute Description data here and click on Save and Submit button, which intur cann IPP RESTFUL service to write data back to Dispute1 data element.

Image4.JPG


On thi sactivity UI, you see that latest edited Disp[ute Description is displayed, means we could successfully wrote data to IPP.

Image5.JPG

You can get the relevant artifacts here.

How to write data to Stardust process-Mashedup UI having popups

In this section we will explore how we can write data, captured in mashedup UI where partial data is captured in popup. For example, the mashed up UI presents a page where user can enter DisputeID and Dispute Date and clicks on a button to open the popup in which the user will select the account number. rest of the data on the main page will be filled in as per selected account number. See images below:

Filled in Dispute data and opened popup to select account number:

Example1.JPG

selected account number 4001, rest of the data belonging to 4001 is fetched:

Example2.JPG

Similarly account number 3001 is selected and data changes accordingly:

Example3.JPG

Example4.JPG

Now we need to embed this UI in Stardus and write the captured data into Stardust process.

The process:

Process 1.JPG

Define a process as shown in the image and define a mashup UI application as shown below:

App1.JPG App2.JPG

Define a data type like shown below (represents the data on mashed up UI)

DataType.JPG

The html, css and js of the UI that will be embeded:

HTML -

<!DOCTYPE html>
<html ng-app='ModalDemo'>
<head >
<link rel="stylesheet" type="text/css" href="./DialogTest.css">
 
 
 
<script src="lib/jquery/jquery-1.9.1.js"></script>
<script src="lib/angular/angular.js"></script>
<script src="js/Interaction.js"></script>
 <script src="js/DialogTest.js"> </script> 
 <script src="lib/misc/xml2js.js"> </script> 
 <script src="lib/jquery/plugins/jquery-cookie.js"></script>
 <script src="lib/jquery/plugins/jquery.url.js"></script>
<meta charset=utf-8 />
<title>Test Popup in Mashup UI</title>
</head>
<body ng-controller='MyCtrl'>
  <div>
 
    <modal-dialog show='modalShown' width='400px' height='60%'>
   Select Account Number<select ng-model="$parent.account" ng-options="t.accountDetails.acctNo for t in accountInfo">
        <option value="">--Select--</option>
      </select>
 
    <button ng-click="saveQuote()">OK</button>
 
      </modal-dialog>
 
  </div>
  Dispute ID <input type="text" ng-model="Dispute.ID">
  Dispute Date <input type="date" ng-model="Dispute.date">
  <button ng-click='toggleModal()'>Locate Account</button>
  <div ng-show="showIt">
  Account Number: <input type="text" ng-model="Dispute.accountInfo.acctNo" disabled="true">
  Account Name <input type="text" ng-model="Dispute.accountInfo.name" disabled="true">
  Account Type <input type="text" ng-model="Dispute.accountInfo.type" disabled="true">
  </div>
 
  <button ng-click="saveData()">Save</button>
</body>
</html>

CSS -

.ng-modal-overlay {
  /* A dark translucent div that covers the whole screen */
  position:absolute;
  z-index:9999;
  top:0;
  left:0;
  width:100%;
  height:100%;
  background-color:grey;
  opacity: 0.8;
}
.ng-modal-dialog {
  /* A centered div above the overlay with a box shadow. */
  z-index:10000;
  position: absolute;
  width: 50%; /* Default */
 
  /* Center the dialog */
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  -webkit-transform: translate(-50%, -50%);
  -moz-transform: translate(-50%, -50%);
 
  background-color: #fff;
  box-shadow: 4px 4px 80px #000;
}
.ng-modal-dialog-content {
  padding:10px;
  text-align: left;
}
.ng-modal-close {
  position: absolute;
  top: 3px;
  right: 5px;
  padding: 5px;
  cursor: pointer;
  font-size: 120%;
  display: inline-block;
  font-weight: bold;
  font-family: 'arial', 'sans-serif';
}

JavaScript -

app = angular.module('ModalDemo', []);
app.directive('modalDialog', function() {
  return {
    restrict: 'E',
    scope: {
      show: '='
    },
    replace: true, // Replace with the template below
    transclude: true, // we want to insert custom content inside the directive
    link: function(scope, element, attrs) {
      scope.dialogStyle = {};
      if (attrs.width)
        scope.dialogStyle.width = attrs.width;
      if (attrs.height)
        scope.dialogStyle.height = attrs.height;
      scope.hideModal = function() {
        scope.show = false;
      };
    },
    template: "<div class='ng-modal' ng-show='show'><div class='ng-modal-overlay' ng-click='hideModal()'></div><div class='ng-modal-dialog' ng-style='dialogStyle'><div class='ng-modal-close' ng-click='hideModal()'>X</div><div class='ng-modal-dialog-content' ng-transclude></div></div></div>"
  };
});
 
app.controller('MyCtrl', ['$scope', '$q', function($scope, $q) {
  $scope.modalShown = false;
  $scope.showIt=false;
  $scope.Dispute ={};
  $scope.Dispute.accountInfo ={};
 
 
  $scope.accountInfo = [{id: "0", accountDetails: {acctNo: "1001", name: "ABC", type: "Savings"}},
	{id: "1", accountDetails: {acctNo: "2001", name: "XYZ", type: "Current"}},
	  {id: "2", accountDetails: {acctNo: "3001", name: "PQR", type: "Savings"}},
	  {id: "3", accountDetails: {acctNo: "4001", name: "MNO", type: "Current"}}];
  var interaction = new Interaction($q, $scope);
 
  $scope.toggleModal = function() {
    $scope.modalShown = !$scope.modalShown;
  };
  $scope.saveQuote = function() {
    console.debug("Dispute: "+ angular.toJson($scope.account));
    $scope.modalShown = !$scope.modalShown;
	$scope.showIt=true;
	$scope.Dispute.accountInfo.acctNo = $scope.account.accountDetails.acctNo;
	$scope.Dispute.accountInfo.name = $scope.account.accountDetails.name;
	$scope.Dispute.accountInfo.type = $scope.account.accountDetails.type;
	};
$scope.saveData = function() {
	var dataToSend = {};
	dataToSend.Dispute = $scope.Dispute;
	console.debug("Dispute: "+ angular.toJson($scope.Dispute));
	interaction.bind();
	console.log("Submitting the request");
               interaction.transfer = dataToSend;
               interaction.post("DisputeType1").then(function () {
                   interaction.completeActivity();
               });
 
 
  };
}]);

The runtime observations:

Start the process, it presents you the mashedup UI:

Popup.JPG

Save.JPG

Fill in the data using popup as well, click on Save. The data entered on the UI is saved into Stardust process and you see that on the next activity UI.

Display.JPG

The artifacts can be found File:DialogTest.zip

Back to the top