Skip to main content
Jump to: navigation, search

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

< Stardust‎ | Knowledge Base‎ | Integration‎ | UI/UIMashup
Revision as of 03:00, 10 August 2013 by Vikash.pandey.sungard.com (Talk | contribs) (New page: 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, ...)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

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

Copyright © Eclipse Foundation, Inc. All Rights Reserved.