Question:
I have a problem with the currency mask with AngularJS.
I found this thread on Stackoverflow in English. The mask works fine by typing in the input and it arrives right in the controller , but when I send formatted from the controller to the screen, it doesn't scroll.
Here 's an example.
- Directive code snippet
app.directive('format', ['$filter',
function ($filter) {
return {
require: '?ngModel',
link: function (scope, elem, attrs, ctrl) {
if (!ctrl) return;
ctrl.$formatters.unshift(function (a) {
return $filter(attrs.format)(ctrl.$modelValue)
});
ctrl.$parsers.unshift(function (viewValue) {
elem.priceFormat({
prefix: '',
centsSeparator: ',',
thousandsSeparator: '.'
});
return elem[0].value;
});
}
};
}
]);
- page code
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>
document.write('<base href="' + document.location + '" />');
</script>
<link href="style.css" />
<script type="text/javascript" src="http://code.jquery.com/jquery-1.8.3.js"> </script>
<script data-require="angular.js@1.2.x" src="http://code.angularjs.org/1.2.9/angular.js" data-semver="1.2.9"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<div>
<br/>Original Example
<input type="text" ng-model="test" format="number" />
<pre>{{test|json}}</pre><br/>
Example 1<input type="text" ng-model="test_2" format="number" />
<pre>{{test_2|json}}</pre><br/>
Example 2<input type="text" ng-model="test_3" format="number" />
<pre>{{test_3|json}}</pre><br/>
</div>
</body>
</html>
Answer:
I don't know where you got that code from, but it's clearly inconsistent. In one function you use the standard AngularJS $filter
, in the other you use a jQuery plugin for number formatting.
The $filter
only runs at the beginning, with the numbers that are already defined in the $scope
, the other runs with each change.
Although I think it's not a good idea to mix jQuery plugins that handle DOM elements mixed with AngularJS filters, if you use the same function in both cases it works:
app.directive('format', ['$filter',
function($filter) {
return {
require: '?ngModel',
link: function(scope, elem, attrs, ctrl) {
if (!ctrl) return;
ctrl.$formatters.unshift(function(a) {
elem[0].value = ctrl.$modelValue
elem.priceFormat({
prefix: '',
centsSeparator: ',',
thousandsSeparator: '.'
});
return elem[0].value;
});
ctrl.$parsers.unshift(function(viewValue) {
elem.priceFormat({
prefix: '',
centsSeparator: ',',
thousandsSeparator: '.'
});
return elem[0].value;
});
}
};
}
]);
Plunker .
EDITION:
Here's a better way to do this thing:
Define a filter
to do the conversion by partially copying the jQuery plugin code:
app.filter('priceformat', function () {
var is_number = /[0-9]/;
var prefix = ''
var suffix = ''
var centsSeparator = ','
var thousandsSeparator = '.'
var limit = false
var centsLimit = 2
var clearPrefix = false
var clearSufix = false
var allowNegative = false
var insertPlusSign = false
if (insertPlusSign) allowNegative = true;
function to_numbers(str) {
var formatted = '';
for (var i = 0; i < (str.length); i++) {
char_ = str.charAt(i);
if (formatted.length == 0 && char_ == 0) char_ = false;
if (char_ && char_.match(is_number)) {
if (limit) {
if (formatted.length < limit) formatted = formatted + char_
} else {
formatted = formatted + char_
}
}
}
return formatted
}
function fill_with_zeroes(str) {
while (str.length < (centsLimit + 1)) str = '0' + str;
return str
}
return function (str) {
var formatted = fill_with_zeroes(to_numbers(str));
var thousandsFormatted = '';
var thousandsCount = 0;
if (centsLimit == 0) {
centsSeparator = "";
centsVal = ""
}
var centsVal = formatted.substr(formatted.length - centsLimit, centsLimit);
var integerVal = formatted.substr(0, formatted.length - centsLimit);
formatted = (centsLimit == 0) ? integerVal : integerVal + centsSeparator + centsVal;
if (thousandsSeparator || $.trim(thousandsSeparator) != "") {
for (var j = integerVal.length; j > 0; j--) {
char_ = integerVal.substr(j - 1, 1);
thousandsCount++;
if (thousandsCount % 3 == 0) char_ = thousandsSeparator + char_;
thousandsFormatted = char_ + thousandsFormatted
}
if (thousandsFormatted.substr(0, 1) == thousandsSeparator) thousandsFormatted = thousandsFormatted.substring(1, thousandsFormatted.length);
formatted = (centsLimit == 0) ? thousandsFormatted : thousandsFormatted + centsSeparator + centsVal
}
if (allowNegative && (integerVal != 0 || centsVal != 0)) {
if (str.indexOf('-') != -1 && str.indexOf('+') < str.indexOf('-')) {
formatted = '-' + formatted
} else {
if (!insertPlusSign) formatted = '' + formatted;
else formatted = '+' + formatted
}
}
if (prefix) formatted = prefix + formatted;
if (suffix) formatted = formatted + suffix;
return formatted
}
})
(Here is the direct values in the code, but you can do different)
Then use this filter in your directive
:
ctrl.$formatters.unshift(function(a) {
return $filter('priceformat')(ctrl.$modelValue)
});
ctrl.$parsers.unshift(function(viewValue) {
return $filter('priceformat')(viewValue)
});