I am trying to implement an INR (Indian Rupee) to USD (US Dollar) currency converter. The view should always display the value in INR. But the model should hold the value in USD.
For that, I have implemented a text box for input. The input will always be given in INR.
And I am using $viewValue and $modelValue attributes of ngModel to handle my problem.
I have a situation where the currency gets calculated in the background on some event. For example. If the currency is stored in the model as 1 dollar. It changes to 2 dollars on some event in the application. In that case, my view shows the value in USD (as $2 in this example) and only if I focus on my text box, the value is shown in INR (as 126 INR).
$viewValue is not being shown in the text box on change event.
Please help me.
.directive('usdInrInput', function($filter, $timeout) {
return {
require: 'ngModel',
link: function(scope, element, attrs, modelCtrl) {
function conversionFunction() {
modelCtrl.$viewValue = modelCtrl.$modelValue * 63;
modelCtrl.$render();
}
element.bind("focus", function(e) {
$timeout(function() {
conversionFunction();
}, 0);
});
element.bind("change", function(e) {
$timeout(function() {
conversionFunction();
}, 0);
});
modelCtrl.$parsers.push(function(inputValue) {
var changedOutput = parseInt(inputValue) / 63;
modelCtrl.$setViewValue(parseInt(inputValue));
modelCtrl.$render();
return parseInt(changedOutput);
});
}
};
})
You should watch for the changes in the model value using scope.$watch
like below:
scope.$watch(function() {
return modelCtrl.$modelValue;
}, function(val) {
conversionFunction();
});
Use a constant for dollar rate so that you can modify at one place if there is a change.
Use $evalAsync
instead of $timeout(function(){},0)
.
Refer evalAsync vs timeout
I deliberately changed the model value after 2 seconds using $timeout
for demo purpose.
angular
.module('myApp', []);
angular
.module('myApp')
.controller('MyController', MyController)
.directive('usdInrInput', usdInrInput);
MyController.$inject = ['$scope', '$timeout'];
function MyController($scope, $timeout) {
$scope.inr = 630;
$timeout(function() {
$scope.inr = 10;
}, 2000);
}
usdInrInput.$inject = ['$filter', '$timeout'];
function usdInrInput($filter, $timeout) {
return {
require: 'ngModel',
link: function(scope, element, attrs, modelCtrl) {
var cRate = 63;
scope.$watch(function() {
return modelCtrl.$modelValue;
}, function(val) {
conversionFunction();
});
function conversionFunction() {
modelCtrl.$viewValue = modelCtrl.$modelValue * cRate;
modelCtrl.$render();
}
element.bind("focus", function(e) {
scope.$evalAsync(function() {
conversionFunction();
});
});
element.bind("change", function(e) {
scope.$evalAsync(function() {
conversionFunction();
});
});
modelCtrl.$parsers.push(function(inputValue) {
var changedOutput = parseInt(inputValue) / cRate;
modelCtrl.$setViewValue(changedOutput);
modelCtrl.$render();
return changedOutput;
});
}
};
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyController as MC">
<input type="text" ng-model="inr" usd-inr-input> {{inr}}
</div>