var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.myNumber = 4;
$scope.config = {
sbMin: 10,
sbMax: 200,
sbPrecision: 2,
sbMaxPrecision: 2
}
});
function countDecimalPlaces(value){
var decimalPos = String(value).indexOf('.');
if(decimalPos === -1){
return 0;
}else{
return String(value).length - decimalPos -1;
}
}
app.directive('sbPrecision', function(){
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attributes, ngModel){
var precision = attributes.sbPrecision;
function setPrecision(){
var value = ngModel.$modelValue;
//since this is just a mask, don't hide decimal values that
//go beyond our precision and don't format empty values
if(value && !isNaN(value) && countDecimalPlaces(value) <= precision){
var formatted = Number(value).toFixed(precision);
ngModel.$viewValue = formatted;
ngModel.$render();
}
}
element.bind('blur', setPrecision);
setTimeout(setPrecision, 0); //after initial page render
}
};
})
app.directive('sbMin', function(){
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attributes, ngModel){
function minimum(value){
if(!isNaN(value)){
var validity = Number(value) >= attributes.sbMin;
ngModel.$setValidity('min-value', validity)
}
return value;
}
ngModel.$parsers.push(minimum);
ngModel.$formatters.push(minimum);
}
};
})
app.directive('sbMax', function(){
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attributes, ngModel){
function maximum(value){
if(!isNaN(value)){
var validity = Number(value) <= attributes.sbMax;
ngModel.$setValidity('max-value', validity);
}
return value;
}
ngModel.$parsers.push(maximum);
ngModel.$formatters.push(maximum);
}
};
})
app.directive('sbNumber', function(){
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attributes, ngModel){
function validateNumber(value){
var validity = !isNaN(value);
ngModel.$setValidity('number', validity)
return value;
}
ngModel.$parsers.push(validateNumber);
ngModel.$formatters.push(validateNumber);
}
};
})
app.directive('sbMaxPrecision', function(){
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attributes, ngModel){
function maxPrecision(value){
if(!isNaN(value)){
var validity = countDecimalPlaces(value) <= attributes.sbMaxPrecision;
ngModel.$setValidity('max-precision', validity);
}
return value;
}
ngModel.$parsers.push(maxPrecision);
ngModel.$formatters.push(maxPrecision);
}
};
})
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>
document.write('<base href="' + document.location + '" />');
</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js@1.2.x" src="http://code.angularjs.org/1.2.13/angular.js" data-semver="1.2.13"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<div ng-form='myForm'>
Config
<ul>
<li ng-repeat='(key,val) in config'>{{key}}: {{val}}</li>
</ul>
<input type='text' ng-model='myNumber' name='number' sb-precision='{{config.sbPrecision}}' sb-min='{{config.sbMin}}' sb-max='{{config.sbMax}}' sb-max-precision='{{config.sbMaxPrecision}}' sb-number />
<div ng-show="myForm.number.$error['min-value']" class='error'>Error: Minimum</div>
<div ng-show="myForm.number.$error['max-value']" class='error'>Error: Maximum</div>
<div ng-show="myForm.number.$error['number']" class='error'>Error: Not a number</div>
<div ng-show="myForm.number.$error['max-precision']" class='error'>Error: Maximum decimal places</div>
{{myForm.number.$error}}
<br><br>
<label>Vanilla text input</label><br>
<input type='text' ng-model='myNumber'/>
</div>
</body>
</html>
/* Put your css in here */
.error{
background: red;
color: white;
padding: 5px 10px;
}
input{
border: 1px solid gray;
padding: 5px 15px;
border-radius: 5px;
font-size: 20px;
font-weight: bold;
}