In this article, we will build a simple EMI Calculator. For building that, we will use JQueryUI slider, normal html textbox components, and AngularJS custom directive will facilitate for communicating between these two components.
Introduction
In this article, we will build a simple EMI Calculator. For building that, we will use JQueryUI slider, normal html textbox components, and AngularJS custom directive will facilitate for communicating between these two components. The final output should be
Straight to Program
Let us first create a folder structure as under
We have the "*.js" files inside the "js" folder and "*.css" inside the "css" folder.
Let us first create the controller "EMICalculatorCtrl.js" and it's content is as under
var emiApp = angular.module("emiApp", []);
emiApp.controller("EMIController", function($scope, $http){
$scope.loanAmount = 1;
$scope.interestRate = 1;
$scope.loanTenure = 1;
$scope.EMI=0.0;
$scope.ishidden= !true;
//this function contains the EMI logic
$scope.calculateEMI = function(){
var loanamt = $scope.loanAmount;
var intrest=$scope.interestRate;
var repaytrm=$scope.loanTenure;
//EMI calculation logic
var rate1 = (parseFloat(intrest)/100)/12;
var rate = 1+rate1;
var interestRate = Math.pow(rate,repaytrm);
var E1 = loanamt*rate1*interestRate;
var E2 = interestRate-1;
var EMI = (E1/E2);
var total_payable=EMI*repaytrm;
var total_interest=(total_payable-loanamt);
//Values to display
$scope.EMI=display2Decimals(EMI);
$scope.interestPayable = display2Decimals(total_interest);
$scope.totalPayable=display2Decimals(total_payable);
$scope.ishidden=!false; } //end function calculateEMI
//this function resets the values to their default
$scope.reset = function(){
$scope.loanAmount = 1;
$scope.interestRate = 1;
$scope.loanTenure = 1;
$scope.EMI=0.0;
$scope.interestPayable=0.0;
$scope.totalPayable=0.0;
$scope.ishidden=!true; } // end function reset
}); //end controller
It's a pretty simple controller whose name is "EMIController" and contains the logic for EMI Calculation. The function calculateEMI holds the logic. The reset function resets the values to their default. The $scope.ishidden is a boolean property that controls the visibility of a particular section in the template which we will encounter shortly.
The display2Decimals(x) is the function that resides under the "utility.js" and it displays 2 decimals. It is as under
function display2Decimals(x){
return Number(parseFloat(x)).toFixed(2);
}
Now let us create the view part by creating a "EMICalculator.html" as under
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>EMI Calculator</title>
<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.0/themes/smoothness/jquery-ui.css">
<link href="css/custom.css" rel="stylesheet" type="text/css">
<script data-require="angular.js@1.4.0-beta.6" data-semver="1.4.0-beta.6" src="https://code.angularjs.org/1.4.0-beta.6/angular.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.0/jquery-ui.min.js"></script>
<script src="js/utility.js"></script>
<script src="js/EMICalculatorCtrl.js"></script>
</head>
<body ng-app="emiApp" ng-controller="EMIController">
<div><h2><font color="blue"><u>EMI Calculator</u></font></h2> </div>
<table border="1">
<tr>
<td bgcolor="D1D39B">
<table>
<tr>
<td colspan="3">
<font color="blue" size="4">Let us understand the financial needs</font>
</td>
</tr>
<tr>
<td>Loan Amount:</td>
<td><div ng-model="loanAmount" style="width:200px;"></div></td>
<td><input type= "text" ng-model="loanAmount" id="txtLoanAmount"> ?</td>
</tr>
<tr>
<td>Interest Rate</td>
<td><div ng-model="interestRate" style="width:200px;"></div></td>
<td><input type= "text" ng-model="interestRate" id="txtIR"> %</td>
</tr>
<tr>
<td>Loan Tenure(months):</td>
<td><div ng-model="loanTenure" style="width:200px;"></div></td>
<td><input type= "text" ng-model="loanTenure" id="txtLT"> Months</td>
</tr>
<tr>
<td></td>
<td></td>
<td>
<input type="button" id="btnCalculate" value="Calculate" class="button blueButton" ng-click="calculateEMI()"/>
<input type="button" id="btnReset" value="Reset" class="button grayButton" ng-click="reset()"/>
</td>
</tr>
</table>
</td>
<td ng-show="ishidden" bgcolor="9BD3BD">
<table>
<tr>
<td colspan="3"><font color="blue" size="4">An indicative amount to pay</font></td>
</tr>
<tr>
<td>Monthly Payment (EMI)</td>
<td>?<input type="text" id="txtEMI" value="{{EMI}}"></td>
</tr>
<tr>
<td>Total Interest Payable</td>
<td>?<input type="text" id="txtIP" value="{{interestPayable}}"></td>
</tr>
<tr>
<td>Total Payment(Principal + Interest)</td>
<td>?<input type="text" id="txtTP" value="{{totalPayable}}"></td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
If we run at this point the view will be
So our slider does not appear. However, we can still perform the operation as under by entering some numeric values and clicking on the Calculate button in which case the result will be
Reset button equally does it job properly
But we need to have the slider to appear and make a two way binding. For that reason, we need to create a directive. The reason is by using directives, we can modify the behavior of a specific DOM element as well as add a new custom element to the DOM. For a detail explanation of AngularJS directive, please read this article. Our directive is as under
emiApp.directive('jqSlider', function() {
return {
restrict: 'A',
scope: {
ngModel: '=',
max: '=?'
},
link: function(scope, elem, attrs) {
scope.$watch('ngModel', function(val) {
$(elem).slider("value", val);
})
return $(elem).slider({
range: "min",
animate: true,
max: scope.max || 100,
value: scope.ngModel,
slide: function(event, ui) {
return scope.$apply(function(){
scope.ngModel = ui.value;
});
}
});
}
};
});
Our directive whose name is jqSlider is a typical isolated scope directive that is using a jQuery plugin and is confined to attributes. scope.ngModel is passed in to the directive through the matching attribute ng-model in the html. The '=' makes it a 2 way binding to it's parent. The link function creates listeners on the DOM model. These listeners keep the view and the model in sync at all times. $watch helps to listen for $scope changes. As for the slider it is a jQuery UI slider and the options like animate, range, max,value, slide etc are documented in their API. The slide( event, ui ) triggers on every mouse move during slide. The value provided in the event as ui.value represents the value that the handle will have as a result of the current movement. Max range can be changed using max option. If max field won't be specified in element it will set a maximum to 100.
Now it's time to bind the same to our view and below is the way we have done
<tr>
<td>Loan Amount:</td>
<td><div jq:slider ng-model="loanAmount" style="width:200px;" max="100000"></div></td>
<td><input type= "text" ng-model="loanAmount" id="txtLoanAmount"> ?</td>
</tr>
<tr>
<td>Interest Rate</td>
<td><div jq-slider ng-model="interestRate" style="width:200px;" max="50"></div></td>
<td><input type= "text" ng-model="interestRate" id="txtIR"> %</td>
</tr>
<tr>
<td>Loan Tenure(months):</td>
<td><div jq_slider ng-model="loanTenure" style="width:200px;"></div></td>
<td><input type= "text" ng-model="loanTenure" id="txtLT"> Months</td>
</tr>
Since we know that, we can use the directive in the view by separating the camel case name (since a directive name is specified in camel case) of the directive, either using a dash, colon, or underscore so we have used it in multiple ways like jq:slider, jq-slider, jq_slider for the demonstration purpose.
Now let us run the application again
So our Slider control is now visible in the DOM structure and we can start playing with our little calculator.
References
Creating Custom Directives
Conclusion
In this article we have learnt the uses of custom directives in AngularJS which facilitates for communicating between JQueryUI and normal Html controls, two way binding and finally build a simple EMI Calculator. Hope this will be useful. Thanks for reading. Zipped file attached.